>  기사  >  백엔드 개발  >  PHP의 오프셋 문자로 인한 취약점 우회 결과에 대한 자세한 설명

PHP의 오프셋 문자로 인한 취약점 우회 결과에 대한 자세한 설명

巴扎黑
巴扎黑원래의
2017-08-11 13:33:561120검색

이 글에서는 PHP의 문자 오프셋 기능으로 인해 발생하는 우회 취약점에 대한 관련 정보를 주로 소개합니다. 이 글에서는 취약점의 형성을 자세히 소개할 뿐만 아니라, 더 중요한 것은 확실한 참고 자료인 복구 방법을 소개합니다. 배움의 가치, 필요한 친구들은 아래를 구경하러 오세요.

php의 문자 오프셋 기능

PHP의 문자열에는 매우 흥미로운 기능이 있습니다. PHP의 문자열도 배열처럼 평가될 수 있습니다.


$test = "hello world";
echo $test[0];

최종 결과는 h입니다.

하지만 위 기능은 가끔 예상치 못한 효과를 낳기도 합니다. 아래 코드를 보세요


$mystr = "hello world";
echo $mystr["pass"];

위 코드의 출력 결과는 왜일까요? 사실 다른 많은 언어와 마찬가지로 PHP의 문자열도 배열처럼 값을 얻기 위해 첨자를 사용할 수 있습니다. $mystr["pass"]를 전달하면 암시적으로 0으로 변환되므로 $mystr[0]의 출력 결과는 첫 번째 문자 h입니다.$mystr["pass"]中pass会被进行隐性类型转换为0,这样$mystr[0]的输出结果就是首字母h.
同样地,如果尝试如下的代码:


$mystr = "hello world";
echo $mystr["1pass"];

输出结果就是e.因为1pass会被隐性类型转换为1,$mystr[1]的输出结果就是第二个字母e.

字符特性造成的漏洞

下面这段代码是在在phpspy2006中用于判断登录时所使用的代码。


$admin['check'] = "1";
$admin['pass'] = "angel";
......
if($admin['check'] == "1") {
....
}

这样的验证逻辑如果利用上述的特性就很容易地就可以被绕过。$admin没有被初始定义为数组类型,那么当我们用字符串提交时phpsyp.php?admin=1abc时,php会取字符串1xxx的第一位,成功绕过if的条件判断。

上面那段代码是一个代码片段,接下来的这段代码是一段完整的逻辑代码,来自于php4fun中第5题,比较有意思。


<?php
# GOAL: overwrite password for admin (id=1)
#  Try to login as admin
# $yourInfo=array( //this is your user data in the db
# &#39;id&#39; => 8,
# &#39;name&#39; => &#39;jimbo18714&#39;,
# &#39;pass&#39; => &#39;MAYBECHANGED&#39;,
# &#39;level&#39; => 1
# );
require &#39;db.inc.php&#39;;

function mres($str)
{
 return mysql_real_escape_string($str);
}

$userInfo = @unserialize($_GET[&#39;userInfo&#39;]);

$query = &#39;SELECT * FROM users WHERE id = \&#39;&#39; . mres($userInfo[&#39;id&#39;]) . &#39;\&#39; AND pass = \&#39;&#39; . mres($userInfo[&#39;pass&#39;]) . &#39;\&#39;;&#39;;

$result = mysql_query($query);
if (!$result || mysql_num_rows($result) < 1) {
 die(&#39;Invalid password!&#39;);
}

$row = mysql_fetch_assoc($result);
foreach ($row as $key => $value) {
 $userInfo[$key] = $value;
}

$oldPass = @$_GET[&#39;oldPass&#39;];
$newPass = @$_GET[&#39;newPass&#39;];
if ($oldPass == $userInfo[&#39;pass&#39;]) {
 $userInfo[&#39;pass&#39;] = $newPass;
 $query = &#39;UPDATE users SET pass = \&#39;&#39; . mres($newPass) . &#39;\&#39; WHERE id = \&#39;&#39; . mres($userInfo[&#39;id&#39;]) . &#39;\&#39;;&#39;;
 mysql_query($query);
 echo &#39;Password Changed.&#39;;
} else {
 echo &#39;Invalid old password entered.&#39;;
}

这道题目网上也仅仅只是给了一个最终的答案,其中的原理都没有说或者没有说得很详细。其实原理就是上面讲到的php的字符特性。

题目要求很简单就是修改admin的密码,admin的id为1。我们需要思考以下几个问题:

  • 如何在更新的时候将id修改为1

  • $userInfo['pass'] = $newPass;这行代码有什么作用,为什么会在if判断语句中存在这种的代码

想通了这两个问题,那么最终的解决方法也有了。将id为8的用户的密码修改为8,然后传入一个userInfo的字符串‘8',突破查询防护,最后利用$userInfo['pass'] = $newPass将id修改为1。

最终的payload就是;

第一次提交, index.php?userInfo=a:2:{s:2:"id";i:8;s:4:"pass";s:12:"MAYBECHANGED";}&oldPass=MAYBECHANGED&newPass=8,目的是将id为8的用户的密码修改为8

第二次提交,index.php?userInfo=s:1:"8";&oldPass=8&newPass=1,这样序列化$userInfo得到的就是字符串‘8',即$userInfo = ‘8' ,这样数据库查询验证就可以通过。之后的if验证也可以通过,通过这行代码$userInfo['pass'] = $newPass;,由于$newpass的值为1,那么上述代码变为了$userInfo['pass'] = 1; ,$userInfo由于一个字符串类型,最后得到的是$userInfo='1' 마찬가지로, 다음 코드를 시도해 보세요.

rrreee출력 결과는 e입니다. 1pass는 암시적으로 1로 변환되므로 $mystr[1]의 출력 결과는 두 번째 문자 e입니다.

캐릭터 특성으로 인한 취약점🎜🎜🎜🎜다음 코드는 phpspy2006에서 로그인 시 사용되는 코드를 판별하는데 사용됩니다. 🎜🎜🎜🎜rrreee🎜위의 기능을 사용하면 이러한 검증 로직을 쉽게 우회할 수 있습니다. $admin은 초기에 배열 유형으로 정의되지 않았으므로 문자열과 함께 phpsyp.php?admin=1abc를 제출하면 PHP는 문자열 1xxx의 첫 번째 비트를 가져와 if 조건부 판단을 성공적으로 우회합니다. . 🎜🎜위 코드는 코드 조각이고 다음 코드는 php4fun의 질문 5에서 나온 완전한 논리 코드입니다. 이는 매우 흥미롭습니다. 🎜🎜🎜🎜rrreee🎜이 질문은 온라인에서 최종 답변만 제공할 뿐, 원리가 설명되지 않거나 자세히 설명되지 않습니다. 사실 그 원리는 위에서 언급한 PHP의 성격적 특성입니다. 🎜🎜🎜질문 요구 사항은 매우 간단합니다. 관리자의 비밀번호를 변경하는 것입니다. 다음 질문에 대해 생각해야 합니다. 🎜
  • 🎜업데이트 중에 ID를 1로 변경하는 방법🎜
  • 🎜$userInfo[ 'pass'] = $newPass;이 코드 줄은 무엇을 합니까? if 판단문에 이런 종류의 코드가 존재하는 이유는 무엇입니까?
🎜이 두 가지 문제를 파악한 후 최종 솔루션도 제공됩니다. ID가 8인 사용자의 비밀번호를 8로 변경한 다음 userInfo 문자열 '8'을 전달하여 쿼리 보호를 돌파하고 마지막으로 $userInfo['pass'] = $newPass를 사용하여 수정합니다. ID는 1입니다. 🎜🎜🎜최종 페이로드는 🎜🎜🎜첫 번째 제출, index.php?userInfo=a:2:{s:2:"id";i:8;s:4:"pass"; 12:"MAYBECHANGED";}&oldPass=MAYBECHANGED&newPass=8, 목적은 ID가 8인 사용자의 비밀번호를 8🎜🎜🎜두 번째 제출, index.php?userInfo=s :1로 변경하는 것입니다. :"8";&oldPass=8&newPass=1, 이런 식으로 $userInfo를 직렬화하여 얻은 문자열 '8', 즉 $userInfo = '8' , 데이터베이스 쿼리 검증을 통과하세요. 후속 if 확인도 이 코드 줄 $userInfo['pass'] = $newPass;를 통과할 수 있습니다. $newpass 값이 1이므로 위 코드는 $userInfo[가 됩니다. 'pass'] = 1; ,$userInfo문자열 유형으로 인해 최종 결과는 $userInfo='1' 이고, 마지막으로 ID는 업데이트됨 사용자 1명의 비밀번호가 사라졌습니다. 🎜🎜🎜🎜수정 방법🎜🎜🎜🎜이러한 취약점을 수정하는 방법도 매우 간단합니다. 데이터 유형을 미리 정의하고 사용 시 예상되는 데이터 유형과 일치하는지 확인하세요. 그렇지 않으면 위에서 언급한 우회 문제가 발생합니다. 동시에 입력을 제어해야 하며, 입력 데이터를 확인하고 함부로 사용해서는 안 됩니다. 🎜

위 내용은 PHP의 오프셋 문자로 인한 취약점 우회 결과에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.