大家好,我是V浪,欢迎来到HW安全之路。今天,我看到了一个CTF题目:攻防世界-XCTF体验题库 : PHP2,但是官方的WP写的不是太清楚,今天我们一起看看这道题。
解题关键点
-
.phps文件 -
浏览器先对URL编码解码一次 -
PHP中的宽松比较和严格比较
什么是.phps文件?
在开始我们的主题之前,让我们先了解一下.phps文件。这可能是很多人都不太熟悉的文件类型。
.phps文件实际上是PHP的源代码文件(PHP Source)。它的主要用途是允许用户直接查看PHP代码。为什么需要这种文件呢?因为普通的.php文件在服务器上执行后,用户通过Web浏览器只能看到执行结果,而无法看到背后的代码。而.phps文件则允许我们直接查看PHP的源代码。
这在学习和调试过程中非常有用。但是请注意,在生产环境中暴露.phps文件可能会带来安全风险,因为它可能泄露敏感信息或程序逻辑。
XCTF题目分析:PHP2
让我们通过一个实际的CTF题目来深入理解PHP编码绕过。题目要求我们访问http://111.198.29.45:45191/index.phps,得到以下PHP代码:
<?php
if("admin"===$_GET[id]) {
echo("<p>not allowed!</p>");
exit();
}
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "admin")
{
echo "<p>Access granted!</p>";
echo "<p>Key: xxxxxxx</p>";
}
?>
乍一看,这段代码似乎设置了一个不可能的条件:如果id等于"admin"就拒绝访问,但是紧接着又说如果id等于"admin"就授予访问权限。这看起来是不是有点矛盾?实际上,这里隐藏着一个巧妙的陷阱。
代码深入分析
让我们逐行分析这段代码:
-
第一个if语句使用了严格比较(===)来检查id是否等于"admin"。如果是,就会显示"not allowed!"并终止程序。 -
接下来,代码对$_GET[id]进行了URL解码。这一步很关键,它改变了id的值。 -
最后,使用宽松比较(==)再次检查id是否等于"admin"。如果是,就授予访问权限并显示key。
这段代码的诡异之处在于,它给了我们一个机会在两次检查之间修改id的值。
PHP中的比较运算符
在继续之前,我们需要理解PHP中"==="和"=="的区别:
-
=== 是严格比较,它不仅比较值,还比较类型。 -
== 是宽松比较,它只比较值,如果类型不同,PHP会尝试进行类型转换。
URL编码:CTF选手的"隐形斗篷"
URL编码是一种将字符转换为可以在URL中安全传输的格式的方法。例如,空格会被编码为"%20"。在我们的例子中,我们可以利用URL编码来"隐藏"admin字符串。
"admin"经过一次URL编码后变成:%61%64%6d%69%6e
如果我们再对这个结果进行一次编码,就会得到:%2561%2564%256d%2569%256e
绕过思路
现在我们的目标很明确:找到一个值,在第一次检查时不等于"admin",但经过URL解码后等于"admin"。
解决方案就是对"admin"进行两次URL编码:
-
第一次编码:admin → %61%64%6d%69%6e -
第二次编码:%61%64%6d%69%6e → %2561%2564%256d%2569%256e
最终,我们应该访问的URL是:http://111.198.29.45:45191/index.php?id=%2561%2564%256d%2569%256e
为什么需要两次编码?
这里有一个关键点需要理解:当我们在浏览器中输入URL时,浏览器会自动对URL进行一次解码。这意味着:
-
浏览器首先将%2561%2564%256d%2569%256e解码为%61%64%6d%69%6e -
这个值传递给服务器,通过第一个if检查(因为它不等于"admin") -
PHP代码中的urldecode()函数再次解码,得到"admin" -
最后一个if语句检查通过,我们获得了访问权限
三次编码尝试
既然两次编码可以成功,那么三次编码会怎么样呢?让我们来试试:
-
第一次编码:admin → %61%64%6d%69%6e -
第二次编码:%61%64%6d%69%6e → %2561%2564%256d%2569%256e -
第三次编码:%2561%2564%256d%2569%256e → %252561%252564%25256d%252569%25256e
我们尝试访问:http://111.198.29.45:45191/index.php?id=%252561%252564%25256d%252569%25256e
结果会是什么呢?
实际上,这个URL不会成功获取flag。原因如下:
-
浏览器解码一次,变成:%2561%2564%256d%2569%256e -
服务器接收到的值是%2561%2564%256d%2569%256e,通过第一个if检查 -
PHP的urldecode()函数解码一次,变成:%61%64%6d%69%6e -
最后的if语句比较时,%61%64%6d%69%6e不等于"admin",所以不会显示flag
四次编码尝试
那么,如果我们进行四次编码呢?
-
第一次编码:admin → %61%64%6d%69%6e -
第二次编码:%61%64%6d%69%6e → %2561%2564%256d%2569%256e -
第三次编码:%2561%2564%256d%2569%256e → %252561%252564%25256d%252569%25256e -
第四次编码:%252561%252564%25256d%252569%25256e → %25252561%25252564%2525256d%25252569%2525256e
我们尝试访问:http://111.198.29.45:45191/index.php?id=%25252561%25252564%2525256d%25252569%2525256e
这个URL同样无法获取flag。原因如下:
-
浏览器解码一次,变成:%252561%252564%25256d%252569%25256e -
服务器接收到的值是%252561%252564%25256d%252569%25256e,通过第一个if检查 -
PHP的urldecode()函数解码一次,变成:%2561%2564%256d%2569%256e -
最后的if语句比较时,%2561%2564%256d%2569%256e不等于"admin",所以不会显示flag
结语
通过这个CTF题目,我们深入探讨了PHP中URL编码绕过的原理。我们发现,在这个特定的例子中,两次编码是获取flag的关键。三次或四次编码虽然看起来更复杂,但实际上无法成功绕过检查。
这个例子告诉我们,在CTF比赛中,有时候最简单的解决方案可能就是正确的。过度复杂化可能会适得其反。同时,它也展示了理解底层原理的重要性。只有真正理解了URL编码、浏览器行为和PHP的解码过程,我们才能成功解决这样的挑战。
下次再见,我是V浪,我们HW安全之路上再相会!
原文始发于微信公众号(HW安全之路):PHP解码陷阱:一次编码不够,两次编码刚好!
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论