BuuCTF做题记录_2
初学者的一些做题记录
[安洵杯 2019]easy_web
进环境发现是这么个东西,URL值得注意:
/index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=

右键源码还可以发现一行字:md5 is funny ~(和md5有关?),估计是被藏到图片后面了。
先在kali下用dirsearch扫一下看看有没有其它文件:
dirsearch -u http://55a28404-cd69-4529-9626-2cd4d4f499d1.node4.buuoj.cn:81/ --delay 3 -t 30
没有什么有用的信息。看看源码:

选中那段字符提示了base64,利用base64转图片工具转换结果就是左上角那张图片。但很明显URL中不是这个东西,应该是将文件名进行了变化。
img参数是大小写字母加数字的组合,猜测是base64,而且随便输个abc再看源码会发现这么个东西:

直接base64解码:MzUzNTM1MmU3MDZlNjc=,又是一串base64再解码:3535352e706e67。看着像十六进制,解一下:555.png
现在这个参数怎么出来的就清楚了:字符串先经过十六进制,再经过两次base64加密。前面猜测这是个include的参数,因为这个img和cmd参数都是在index.php下的,那就直接看index.php是啥东西(用同样的方法转换):img=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3
1 | |
最开始正则匹配\那里没弄明白,一开始觉得|\\|\\\\|这俩东西就会把\这东西给匹配了,去网上搜了wp发现都说直接l\s就能绕过。后面发现自己想错了,|\\|\\\\|这东西并不会匹配到\,需要\\\才行。举个栗子:
1 | |
这时我们传?b=任何值都会报错:
**Warning**: preg_match(): No ending delimiter '/' found in **D:\phpstudy_pro\WWW\wow.php** on line **4**
电脑没有找到/这个结束定界符。想了下原因,既然\这个东西是转义字符,那么\\只不过是一个转义后的\,这个东西和/组合到一起会去正则匹配/这个东西,这样一来导致缺少尾部界定符。修改一下:
1 | |
这时不会报错而且回去正则匹配/,现在思路就清晰了:如果我们想正则匹配\,那么就需要\\\才行:
1 | |
现在再看看这段正则匹配怎么个事:
1 | |
注意|\\|\\\\|,这东西相当于去匹配|\\\\。因为前两个\\回去转义后面那个分隔符|,再加上\\\\,然后以|收尾:
1 | |
我们只有传?b=|\\\\才会echo检测到,所以我们仍可以利用\进行命令拼接,比如l\s,l\s%20/,c\at%20fl\ag等。
在burp中如果使用空格会被识别为其他的参数,所以这里空格用%20替换。
综上,可以进行命令执行,得到flag。
[GWCTF 2019]我有一个数据库

???
用dirsearch扫一下,发现robots.txt还有/phpmyadmin/,看看都有啥:
robots.txt提示phpinfo.php,/phpmyadmin/不登录可以直接访问数据库管理界面:

后面就无处下手了。。去网上查了一下wp,这题考的是phpmyadmin4.8.1版本存在任意文件读取漏洞:include $_REQUEST['target'];
直接利用现成的payload:target=db_sql.php%253f/../../../../../../../../etc/passwd,发现可以进行任意文件读取。后面再翻翻flag的位置就行了。
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../flag
flag{14b071f4-a712-4a14-b3ce-d6bc07df4506}
不过我看文章中都写了$_REQUEST['target'],但尝试POST提交拿不到结果。而且在?target前加个/就白屏了。。不过右键看源码还是能看到回显,不知道咋回事。
[MRCTF2020]PYWebsite
进环境发现有这么个东西:

看下源码:
1 | |
看完想了一下,直接去flag.php看看怎么个事:
http://node4.buuoj.cn:25037/flag.php

提示保存了购买者的IP,可以利用XFF伪造。除了购买者和我自己,没有人可以看到flag,直接XFF 127.0.0.1试试?

[ASIS 2019]Unicorn shop
题目提示unicode和python
了解了下unicode:
1 | |

试了试购买前三种都提示:

买最后一个会显示:Only one char(?) allowed!,意思是这个Price只能填一个字符?源码里面有下面这段提示:
1 | |
去网上找了下wp,和提示的Unicode有关:先把Item ID和Price都放空,看看报错信息:

可以发现他用了unicodedata.numeric()函数去处理这个price,这东西会获取 Unicode 字符的数值表示,比如:
1 | |
然后利用这个网站https://www.compart.com/en/unicode找numeric value大于1337的就行

直接把目标符号复制粘贴到Price价格里就好了。
补充:这题源码里有段price = urlib.unquote(price).decode('utf-8'),即先把输入url解码后再经过utf-8编码赋值给price,然后用unicodedata.numeric()处理price。
所以可以通过先找目标字符的utf-8编码,把它转换成对应的url形式(把0x换成%就行,再去掉空格)
[网鼎杯 2020 朱雀组]Nmap(这题还没做完先放一放)

一个端口扫描网站,初步猜测存在RCE或者文件包含。
先看源码里有啥:<!-- flag is in /flag -->
嗯,那应该是要RCE了
随便输个127.0.0.1看看怎么个事:

提供了一些端口信息,这个URL值得注意:
/result.php?f=f7909
to index就是返回首页,to list是这么个界面:

File那一栏就是result.php底下的f参数,尝试了六次发现文件名应该五六位十六进制的随机数。他这东西的运行过程大概就是扫描目标IP的端口,然后给结果五位十六进制随机数的文件名,存储起来如果想读取利用result.php进行了文件包含。
不过结果中存在一个警告,抓包看看怎么个事:

首先,输入IP后会进行302重定向。
如果随便更改f的值呢?会出现下面的报错:

simplexml_load_file()会把把XML 文档载入对象中,所以应该是把结果以XML文件的形式存储。
127.0.0.1;ls提示Host maybe down,换成127.0.0.1 ;发现会给;前加一个\进行转义变成127.0.0.1 \;,但如果没有这个空格会直接报错host maybe down
后面捣鼓半天不知道怎么弄了,找了wp发现是自己思路错了,这题考的是Nmap的一些用法。
还没做完先放一放。。一看这两个escape函数我就头疼
[NPUCTF2020]ReadlezPHP
进环境发现是这么个东西,尝试右键看源码没啥反应,直接在URL加view-source:

发现有这么个东西,直接访问:./time.php?source(本来还想拿dirsearch扫来着,看来没必要了)
1 | |
现在思路就清晰了,直接构造我们想执行的命令就行:
1 | |
不过没有运行?后面换了其它的命令也是没有回显。感觉是被过滤了(可以执行命令的函数有很多),但也想不到其它思路,去看了其它师傅的wp发现要用assert这个函数,正好借这个机会梳理一下RCE中常用的几个命令执行函数,还有他们之间的关系:
我个人认为最常见的肯定是eval和system,先说下它俩之间最大的差别(个人观点):
eval类型函数是代码执行而不是命令执行
system类型函数是命令执行而不是代码执行,举几个栗子:
1 | |
1 | |
1 | |
1 | |
1 | |
接下来这个echo $b($a)也有点说法,这东西有人叫它变量函数,比如:
1 | |
先说下这个assert函数:
1 | |
至于为什么不能只单纯用一个eval,原因是PHP中不能以变量函数的形式去调用它:eval 属于PHP语法构造的一部分,并不是一个函数,所以不能通过 变量函数 的形式来调用。
可以上传一个简单的一句话木马:
1 | |
先用scandir函数看看根目录底下都有啥东西:
time.php?data=O%3A8%3A"HelloPhp"%3A2%3A{s%3A1%3A"a"%3Bs%3A19%3A"eval(%24_REQUEST[3])%3B"%3Bs%3A1%3A"b"%3Bs%3A6%3A"assert"%3B}&3=var_dump(scandir('/'));
注意要用分号结尾!否则不能被执行!用var_dump的原因是这函数会返回数组

有个FIag_!S_it,system被禁了所以用不了cat,尝试file_get_contents直接读,但是假的flag:
echo(file_get_contents('/FIag_!S_it'));
注意这个不能写成:
echo("file_get_contents('/FIag_!S_it')");,这个命令会显示file_get_contents('/FIag_!S_it')

用蚁剑连接看看怎么个事:
http://1730f221-b91f-4b8b-b19d-8ce0eb0211e5.node4.buuoj.cn:81/time.php?data=O%3A8%3A%22HelloPhp%22%3A2%3A{s%3A1%3A%22a%22%3Bs%3A19%3A%22eval(%24_REQUEST[3])%3B%22%3Bs%3A1%3A%22b%22%3Bs%3A6%3A%22assert%22%3B}


没有任何东西,应该是没权限。
看师傅们的wp都是去phpinfo()找,ctrl+f搜索就行。
payload:time.php?data=O%3A8%3A"HelloPhp"%3A2%3A{s%3A1%3A"a"%3Bs%3A19%3A"eval(%24_REQUEST[3])%3B"%3Bs%3A1%3A"b"%3Bs%3A6%3A"assert"%3B}&3=phpinfo();
再补充下scandir()这个函数:
scandir()函数会接受一个目录路径作为参数,并返回一个数组,包含该目录中的所有文件和子目录的名称。返回的数组中,每个元素表示目录中的一个文件或子目录的名称。
1 | |
结果:

[CISCN2019 华东南赛区]Web11
进去发现是个和IP有关的页面,右上角显示了我们的公网IP:

下面甚至有请求包的完整信息:

既然它获取了IP那大概率可以想到XFF伪造,抓包试试看:

确实是这样,但有什么用?这题明显不是利用XFF的SQL注入,想到之前做过一道好像叫cookie is so stable的题,通过模板注入解题,试试看:
X-Forwarded-For:{7*7}

还真是,老规矩先判断是什么类型的,再试试:X-Forwarded-For:a{*comment*}b

应该是Smarty模板,个人认为这个模板最大的特点就是可以直接执行命令,在XFF直接输入:{system('ls /')}去找flag,然后直接cat读就行了。
其实这题底下有行小字提示了:Build With Smarty !

[CISCN 2019 初赛]Love Math
看题目可能跟MD5有关?
直接给了源码:
1 | |
这题花了很长时间没解出来,因为一直在想怎么去绕过黑名单和正则匹配去执行命令,后面看了wp才知道要利用给的函数去做,参考了这位师傅的文章https://www.cnblogs.com/20175211lyz/p/11588219.html
https://www.anquanke.com/post/id/220813#h3-2
思路一
主要是base_convert()函数和dechex函数的使用:

可以看到这东西的范围是2-36,正好覆盖了0-9,a-z。dechex函数用于将十进制数转换为十六进制数。它接受一个十进制数作为参数,并返回一个表示相应十六进制数的字符串。
先看wp怎么写的:
1 | |
payload:
/index.php?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){0}(($$pi){1})&0=system&1=<command>
因为师傅写的很详细还是很好理解的:先利用base_convert函数转出一个hex2bin,再利用dechex转出一个十六进制字符串,前函后参组合转出_GET.
base_convert遇到36进制时会转换成小写字母,举个栗子:
1 | |
后面就很好理解了,它会直接eval过滤后的c。执行后c=$pi=_GET,$$pi=$_GET。再利用$_GET{0}($_GET{1})去执行函数就好。即使[]被过滤了仍可以使用{}绕过。构造完_GET后还需要引入一个变量来作为函数执行, 例如 $a(), 我们用 $pi 以绕过白名单的检测。
思路二
利用getallheaders去解题:
payload:/index.php?c=$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})注意这个getallheaders的用法
首先为什么是30进制(参考了bfengj师傅的文章):
1 | |
system(getallheaders(){1}),这个{1}应该是获取包头为1部分的值,我说应该是因为没找到相关文章说这个东西。。大多直接给了payload(估计师傅们觉得这问题没必要说,哈哈)。我自己尝试把1改成别的再改对应包头也能执行命令(我这里改成了333):

[BSidesCF 2019]Kookie

让我们以admin的身份登录,然后提示已经有cookie/monster这两个账户了,选cookie这个用户名再随便输个密码登录一下看看:

可以看到行为/用户名/密码都是直接以GET方式传过去的(源码也有相应提示),那我们直接把username后面的东西删掉看看能否登录:
/?action=login&username=cookie
这个登不上去,换monster试试:
/?action=login&username=monster
登录成功:

抓包看看cookie:

发现cookie字段中username=monster,直接改成admin:

应该是新生赛的题目