PHP代码审计分段讲解(1)
PHP源码来自:https://github.com/bowu678/php_bugs
快乐的暑期学习生活+1
01 extract变量覆盖
<?php $flag='xxx';
extract($_GET);
if(isset($shiyan))
{
$content=trim(file_get_contents($flag));
if($shiyan==$content)
{
echo'ctf{xxx}';
}
else
{
echo'Oh.no';
}
} ?>
变量覆盖漏洞:自定义的参数值替换原有变量值的情况
extract()函数介绍:https://www.runoob.com/php/func-array-extract.html
源代码使用extract($_GET)接收了GET请求传输进来的数据,并且将键名和键值转换为变量名和变量的值,可以看到有$flag变量是在extract之前就定义了的,file_get_contents() 函数把整个文件读入一个字符串中。正常的代码流程读取$flag文件并且去除两边的空格,将结果存储在$content里面,然后使用我们传入的$shiyan变量与$content进行比较,如果相同的话就输出flag,否则输出 Oh no。
如果$flag文件存在的话,因为是题目服务器上的文件,我们没有权限知道里面的内容,在其读取到$content变量里面的时候,更无法与其进行比较,进而获取flag
突破点在于:extract()函数在flag变量值的下面,所以我们可以以GET方式对$flag重新赋值,进而读取一个不可能存在的文件,使$content为空,传入$shiyan变量也为空,即可获取flag
如图:

02 绕过过滤的空白字符
<?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.txt die("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;
代码比较长,但是不慌,从上往下看,用到函数的时候再到函数里再去看构造
if(!isset($_GET['number'])){
header("hint:26966dc52e85af40f59b4fe73d8c323a.txt"); //HTTP头显示hint 26966dc52e85af40f59b4fe73d8c323a.txt
die("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 — 使用反斜线引用字符串
}
}
在这里使用GET方式传入number参数的值,同时也可以通过GET或者POST的方式传入其他参数的值
if(is_numeric($_REQUEST['number'])) //is_numeric — 检测变量是否为数字或数字字符串
{ $info="sorry, you cann't input a number!"; }
使用is_numeric函数检测传入的$number是否为数字,不能传入数字
elseif($req['number']!=strval(intval($req['number']))) //intval — 获取变量的整数值
{ $info = "number must be equal to it's integer!! "; }
intval函数获取变量的整数值
strval — 获取变量的字符串值
这一步需要传入的是整数,也就与第一个if条件相矛盾
这两部分都可以通过在number参数前加%00进行绕过
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;
这一部分第一步需要$value1和$value2相等,strrev函数令number取反,所以$number需要是一个回文数字,如12321,第二步需要绕过is_palindrome_number函数,使其返回值为false,其函数为:
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;
}
该函数将$number数字转换为字符串,然后进行首尾依次比较,首尾相同则返回true,否则返回false。
这里看上去是矛盾的,既要令$number是回文,又要令$number不是回文。
分析可知,在绕过else里面的限制时,我们需要的是一个能逃逸出intval和is_numeric函数,却逃逸不出is_palindrome_number函数的字符
在P师傅的博客中,是通过查看PHP底层源代码进行分析的:
https://www.leavesongs.com/PENETRATION/some-sangebaimao-ctf-writeups.html
发现了\f字符能够进行实现上述的逃逸,看源代码当然很好,但是这里我们采用FUZZ的方法,python3脚本
import requests for i in range(256):
url = "http://127.0.0.1/php_bugs-master/02.php?number=%00%{:02X}12321".format(i)
rep=requests.get(url)
if "x" in rep.text:
print(url)
运行之后结果为:

所以能够逃逸出来的字符为%0C 和 %2B,使用以上payload读取flag
好像这道题是SCTF2016的一道代码审计题目,挺不错的 :D
PHP代码审计分段讲解(1)的更多相关文章
- PHP代码审计分段讲解(14)
30题利用提交数组绕过逻辑 本篇博客是PHP代码审计分段讲解系列题解的最后一篇,对于我这个懒癌患者来说,很多事情知易行难,坚持下去,继续学习和提高自己. 源码如下: <?php $role = ...
- PHP代码审计分段讲解(13)
代码审计分段讲解之29题,代码如下: <?php require("config.php"); $table = $_GET['table']?$_GET['table']: ...
- PHP代码审计分段讲解(11)
后面的题目相对于之前的题目难度稍微提升了一些,所以对每道题进行单独的分析 27题 <?php if(!$_GET['id']) { header('Location: index.php?id= ...
- PHP代码审计分段讲解(12)
28题 <!DOCTYPE html> <html> <head> <title>Web 350</title> <style typ ...
- PHP代码审计分段讲解(10)
26 unserialize()序列化 <!-- 题目:http://web.jarvisoj.com:32768 --> <!-- index.php --> <?ph ...
- PHP代码审计分段讲解(9)
22 弱类型整数大小比较绕过 <?php error_reporting(0); $flag = "flag{test}"; $temp = $_GET['password' ...
- PHP代码审计分段讲解(8)
20 十六进制与数字比较 源代码为: <?php error_reporting(0); function noother_says_correct($temp) { $flag = 'flag ...
- PHP代码审计分段讲解(7)
17 密码md5比较绕过 <?php if($_POST[user] && $_POST[pass]) { mysql_connect(SAE_MYSQL_HOST_M . ': ...
- PHP代码审计分段讲解(6)
14 intval函数四舍五入 <?php if($_GET[id]) { mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_M ...
随机推荐
- 【QT】 Qt多线程的“那些事”
目录 一.前言 二.QThread源码浅析 2.1 QThread类的定义源码 2.2 QThread::start()源码 2.3 QThreadPrivate::start()源码 2.4 QTh ...
- mingw32环境下链接库找不到问题
本人在win10下安装了mingw环境,以方面windows下测试gcc编译器构建一些开源组件.但是windows系统下遇到了一些编译问题. 1. 问题现象 一次手写的Makefile遇到了如下编译错 ...
- 在Linux深度系统deepin下安装docker
wget -qO- https://get.docker.com/ | sh wget -qO- https://get.docker.com/ | sh
- quic 分析 1
问题1:quic握手过程是怎样的? 怎样节约握手时间?握手时间多少个RTT?握手过程成涉及到哪些概念以及变量(代码) 0~1 RTT握手过程 QUIC握手的过程是需要一次数据交互,0-RTT时延即 ...
- linux tcp/ip 参数解析
后面整理相关信息/* * TCP option lengths */#define TCPOLEN_MSS 4 //只能出现在SYN段中#define TCPOLEN_W ...
- MYSQL学习(三) --索引详解
创建高性能索引 (一)索引简介 索引的定义 索引,在数据结构的查找那部分知识中有专门的定义.就是把关键字和它对应的记录关联起来的过程.索引由若干个索引项组成.每个索引项至少包含两部分内容.关键字和关键 ...
- SQL Server将查询出数据进行列转行操作
在日常的SQL Server数据查询时经常会遇到需要将数据列转换成行的操作,现将自己学习的列转行SQL语句举例如下: --首先查询语句 SELCT * FROM YXBAK..TBYJKSTEMP ...
- c# 调用Go 动态库
[StructLayout(LayoutKind.Sequential)] public struct GoMem { public IntPtr data; public UInt64 len; p ...
- web安全入门--入门条件
介绍:网络安全由习大大提出,是继海.陆.空.外太空的第五大作战领域,也是一个关系国家安全和主权.社会稳定.民族文化继承和发扬的重要问题.其重要性,正随着全球信息化步伐的加快越来越重要.接下来,我向大家 ...
- Netty源码分析之ByteBuf(二)—内存分配器ByteBufAllocator
Netty中的内存分配是基于ByteBufAllocator这个接口实现的,通过对它的具体实现,可以用来分配我们之前描述过的任意类型的BytebBuf实例:我们先看一下ByteBufAllocator ...