PHP代码审计学习 项目地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?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()
: 从数组中将变量导入到当前的符号表。该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。该函数返回成功设置的变量数目file_get_contents()
: 将整个文件读入一个字符串trim()
: 去除字符串首尾处的空白字符(或者其他字符)
trim ( string $str , string $character_mask = “ \t\n\r\0\x0B” ) : string
“ “ (ASCII 32 (0x20)),普通空格符
“\t” (ASCII 9 (0x09)),制表符
“\n” (ASCII 10 (0x0A)),换行符
“\r” (ASCII 13 (0x0D)),回车符
“\0” (ASCII 0 (0x00)),空字节符
“\x0B” (ASCII 11 (0x0B)),垂直制表符
payload: http://127.0.0.1/php_bugs/01.php?shiyan=&flag=
最开始$flag
的值为xxx
,通过函数extract()
把$flag
和$shiyan
的值覆盖为空,则在if
判断中为true
打印flag值
0x02 绕过过滤的空白字符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 <?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;
is_string()
: 检测变量是否是字符串addslashes()
: 使用反斜线引用字符串strval()
: 获取变量的字符串值intval()
: 获取变量的整数值is_numeric()
: 检测变量是否为数字或数字字符串
payload: http://127.0.0.1/php_bugs/02.php?number=%00%0c1
is_numeric()
可以通过\0(%00)
绕过,从而试得is_numeric($_REQUEST['number'])
为false
由于trim()
函数没有过滤\f(%0c)
,而intval()
函数跳过\f(%0c)
,导致$value1
和$value2
都为相等,进入到is_palindrome_number
函数成功通过$number[$i] !== $number[$j]
检测返回false
,最终进入到获取$flag
最后的else
里,输出flag
值
0x03 多重加密 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 <?php include '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'); } ?>
array_merge()
: 合并一个或多个数组base64_decode()
: 对base64编码的数据进行解码base64_encode()
: 使用base64对数据进行编码gzuncompress()
: 解压缩一个压缩的字符串gzcompress()
: 压缩一个字符串unserialize()
: 对单一的已序列化的变量进行操作,将其转换回 PHP 的值serialize()
: 对一个 PHP 的值进行序列
payload: http://127.0.0.1/php_bugs/03.php?token=eJxLtDK0qi62MrFSKi1OLVKyLraysFLKTM4ozSvMLFWyrgUAo4oKXA==
1 2 3 4 5 6 7 <?php $arr = array("user" => "ichunqiu"); $token = base64_encode(gzcompress(serialize($arr))); print_r($token); // eJxLtDK0qi62MrFSKi1OLVKyLraysFLKTM4ozSvMLFWyrgUAo4oKXA== ?>
题目环境不全,描述解题方法
获取flag
的值需要满足$login['user'] === 'ichunqiu'
, $login
的值来自
1 $login = unserialize(gzuncompress(base64_decode($requset['token'])));
将token
的值先经过了base64_decode
解码,然后通过gzuncompress
解压缩字符串,字符串经过unserialize
反序列号赋值
而$requset['token']
的值来源于$requset = array_merge($_GET, $_POST, $_SESSION, $_COOKIE);
也就是说,我们将user
的值通过数组形式进行赋值
1 $arr = array("user" => "ichunqiu");
然后通过上述的编码方式编码回去,依次进行序列化,字符串压缩和base64编码
1 $token = base64_encode(gzcompress(serialize($arr)));
0x04 SQL注入_WITH ROLLUP绕过 0x05 ereg正则%00截断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?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>'; } } ?>
ereg()
: 用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字母的字符是大小写敏感的strpos()
: 函数查找字符串在另一字符串中第一次出现的位置
解法一:
ereg()只能处理字符串,提交password
为数组形式可绕过,下文中的strpos()
判断不能是数组,所以返回为null
,而null
不等于false
成立,输出flag
payload: ?password[]=
解法二: