Auditoría de código PHP 13 - tipo débil

1. Base de tipo débil de PHP

Introducción:

Un lenguaje débilmente tipado no tiene restricciones en el tipo de datos de una variable. Puede asignar una variable a cualquier otro tipo de variable en cualquier momento, y la variable también se puede convertir a cualquier otro tipo de datos. En este momento, la conversión de tipo, la comparación de diferentes tipos y el paso de parámetros irrazonable provocarán resultados de ejecución inesperados y evitarán las defensas.

1. Tipos débiles de operadores de comparación

Para PHP, hay dos operadores de comparación ==y===

  • ==: al comparar, el tipo de cadena se convertirá primero en el mismo y luego se comparará
  • ===: al comparar, primero juzgará si los tipos de las dos cadenas son iguales y luego comparará

Al usar "==", si compara un número y una cadena o compara una cadena que involucra contenido digital, la cadena se convertirá en un valor y la comparación se realizará de acuerdo con el valor, que durante mucho tiempo ha llevado al tipo débil problema de comparación de valores. Las situaciones comunes son las siguientes:

  • defecto de comparación hash

    示例:
    "0e861580163291561247404381396064" == "0e509367213418206700842008763514"   //true
    导致这种问题产生的原因在于,hash字符串以0开头,会认为这是一个数字,然后解析为0*10^n,最终两变得的结果都为0,所以判断结果为true
  • Error de comparación de conversión hexadecimal

    示例:
    "0x1e240"=="123456"     //true
    导致这个问题是原因也是由于php将0x1e240识别为数字,就会先将0x1e240准换为整数在进行比较。
    
  • conversión de tipo

    <?php
    $test=1 + "10.5"; // $test=11.5(float)
    $test=1+"-1.3e3"; //$test=-1299(float)
    $test=1+"bob-1.3e3";//$test=1(int)
    $test=1+"2admin";//$test=3(int)
    $test=1+"admin2";//$test=1(int)
    ?>
    PHP手册:当一个字符串被当作一个数值来取值,其结果和类型如下:
    		如果该字符串没有包含’.,’e’,E’ 并且其数值值在整形的范围之内该字符串被当作 int 来取值,其他所有情况下都被作为 float 来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为 0
  • suplantación de identidad booleana

    示例情况:
    $data ={
          
          user:true;pass:true}
    if ($data['user'] == 'admin' && $data['pass']=='secirity'){
          
          
        print_r('logined in as bool'."\n");
    }
    //运行结果:logined in as bool
    原理解释:
      当我们传入的user和pass=true时,会认为时采用的布尔类型的数值进行比较,就会将"admin""secruity"转换为布尔型进行比较,而在PHP中,只有0false0.0、不包含任何元素的数组、不包含任何变量的对象、null会被准换为0(false),其他的都会被准换位1(true)。所以最终的结果就是if(1=1 && 1=1),结果为true.
    
  • problema de conversión digital

    示例1$user_id = ($_POST['user_id']);
    if ($user_id == "1")
    {
          
          
        $user_id = (int)($user_id);
        #$user_id = intval($user_id);
        $qry = "SELECT * FROM `users` WHERE user_id='$user_id';";
    }
    $result = mysql_query($qry) or die('<pre>' . mysql_error() . '</pre>' );
    在这里,当我们传入的数字ID=0.9999999999999999999999,在进行if判断的时候,会将ID转换为1继进行判断,而在查询的时候,数据库会进行向下取整,认为ID=0,从而查询ID=0的用户数据。
     
    示例2:intval强制转换的弱类型问题
    if (intval($qq) === '123456')
    {
          
          
        $db->query("select * from user where qq = $qq")
    }
    如果采用上述方法,intval将转换所有数字直到遇到非数字为止,随意如果我们传入123456 union select version()inval($qq)的值将会是123456,但是在查询的时候,由于没有使用intval准换后的ID,就导致了注入。
    

2. Funciones con problemas de tipos débiles

  • MD5(),sha1()

    问题:这两个函数不能处理数组,对于传入的数组,返回结果为null,比如下面这种情况,就会导致null==null,结果为true.
    $str = array('123' =>123 );
    $test=array('234' =>234);
    if(md5($str)==md5($test)){
          
          
        echo 'success.......';
    }
    
  • strcmp()

    这个函数的作用是比较两个字符串并且区分大小写
    当字符串1大于字符串2就返回>0,当字符串1小于字符串2就返回<0,相等则返回0
    示例:strcmp($str1,$str2)
    绕过方法:通过传入数组的方法,让该函数处理时发生错误,返回结果0,导致绕过。
    示例情况:
    <?php
    		$POST=$_POST[name];         //POSTdata: name[]=1
        $pass1="ASDSADSADAD";
        if(isset($_GET['pass'])){
          
          
            if(strcmp($_GET['pass'],$pass1)==0){
          
          
                echo "success,233";
            }else{
          
          
                echo " fail,322";
            }
        }
    ?>
    //result : success,233
    
  • cambiar()

    问题在于switch选择的时候,处理的变量会被强转为int类型。
    <?php
      $a=$_GET['$a'];
      switch ($a) {
          
          
      case 1:
        echo "error";
        break;
      case 2:
        echo "success";
        break;
      }
    ?>
    //当我们传入2abcdef时,输出结果为:success
    
  • en_matriz()

    问题点:
    函数格式 in_array(需要在数组内搜索的数值,被搜索的数组,一个可选参数,设置TURE检查数据和数组值类型是否相同),这个函数的作用是检查数组中是否存在某个值,当没有最后的检测参数为true时,默认为松散比较,导致弱类型,传入不同数据类型来绕过。示例:
    <?php
    $array=[1,2,3];
    var_dump(in_array('aaa', $array));   //false
    var_dump(in_array('1aa', $array));   //true
    ?>
    
  • matriz_buscar()

    该函数的作用是在数组中搜索某个键值,并返回键名
    函数格式 array_search(要搜索的键值,被搜索的数组,设置TURE检查数据和数组值类型是否相同)
    当数据类型不同会先把原有数据的数据类型的进行转换,导致弱类型的产生
    示例:
    <?php
    $array=[1,2];
    var_dump(array_search('aaa', $array));   //boolen false
    var_dump(array_search('2aa', $array));   //int 1
    ?>
    
  • Otras funciones: intval(), is_numeric, strpos, etc. tienen problemas similares.

3. Lagunas históricas causadas por tipos débiles

  • Vulnerabilidad de restablecimiento de contraseña de usuario arbitrario de DedeCMS
  • Restablecimiento de contraseña de ZPanel
  • Inyección secundaria PHPYun
  • Inyección SQL de Piwigo
  • Inyección HDwikiSQL

2. Análisis de las preguntas de muestra del CTF

1. Derivación MD5

Mira el código primero:

 1 <?php
 2 if (isset($_GET['Username']) && isset($_GET['password'])) {
    
    
 3     $logined = true;
 4     $Username = $_GET['Username'];
 5     $password = $_GET['password'];
 6 
 7      if (!ctype_alpha($Username)) {
    
    $logined = false;}  //usernam必须为字符型
 8      if (!is_numeric($password) ) {
    
    $logined = false;}  //passwor必须为数值型
 9      if (md5($Username) != md5($password)) {
    
    $logined = false;}
10      if ($logined){
    
    
11     			echo "successful";
12      }else{
    
    
13            echo "login failed!";
14      }
15 }
16 ?>    

Se puede ver que aquí se debe pasar un parámetro variable de nombre de usuario y contraseña, y si el nombre de usuario no es igual a la contraseña, el valor md5 debe ser igual a omitir. Así que aquí necesitamos encontrar un par de colisiones Md5. Dado que aquí se usa "!=", puede encontrar el par de colisión cifrado que comienza con 0e para omitir.

Aquí se utiliza un par de colisión:

username= QNKCDZO 
password= 240610708 

Resultados de la prueba:

inserte la descripción de la imagen aquí

2, array_srach () puente

código fuente:

<?php
 if(!is_array($_GET['test'])){
    
    exit();}
 $test=$_GET['test'];
 for($i=0;$i<count($test);$i++){
    
    
    if($test[$i]==="admin"){
    
    //使用强比较判断数组中是否存在admin键值,如果存在,输出error。
        echo "error";
        exit();
    }
     $test[$i]=intval($test[$i]);
 }
 if(array_search("admin",$test)===0){
    
    
     echo "flag";
 }else{
    
    
    echo "false";
 }
?>

Análisis: Aquí necesitamos usar la característica de array_search() que la búsqueda de tipo débil cambiará el tipo de número.Dado que $test[$i]=intval($test[$i]);los valores en la matriz se cambiarán a plástico, entonces en array_serch, el administrador se convertirá a plástico para comparar , y el administrador convertido es "0", por lo que debemos hacer que la matriz entrante no contenga administrador, y el primer valor se puede omitir después de convertirlo a "0" por intval. Aquí dejamos directamente que la matriz test[]=0 para pasar por alto O podemos omitirlo configurando el primero de la matriz en cualquier cadena que no comience con un número.

Resultados de la prueba:

inserte la descripción de la imagen aquí

3. Ejemplo: HDWIKI insípida inyección SQL

Código fuente del punto de vulnerabilidad:

function dofocus(){
    
    
                $doctype = $this->get[2];  //通过GET方式获取doctype
                switch($doctype){
    
    
                        case 2:
                                $type = 'hot';
                                $navtitle = $this->view->lang['hotDoc'];
                                break;
                        case 3:
                                $type = 'champion';
                                $navtitle = $this->view->lang['wonderDoc'];
                                break;
                        default:  //如果 $doctype 不等于2或者3,则会进入默认程序,设置 $docty2=1,所以需要绕过switch检测,不进入默认程序。这里就可以利用switch弱类型绕过。
                    						$doctype = 1;
                                $navtitle = $this->view->lang['focusDoc'];
                                $type = 'focus';
                }
                $url = 'list-focus-'.$doctype;
                $this->get[3] = empty($this->get[3]) ? NULL : $this->get[3];
                $page = max(1, intval($this->get[3]));
                $start_limit = ($page - 1) * $this->setting['list_focus'];
                $total=100;
                $num=10;
                $count=$this->db->fetch_total('focus',"type=$doctype");  //漏洞点,拼接了$doctype到sql语句中。
                $count=($count<$total)?$count:$total;
                $list=$_ENV['doc']->get_focus_list($start_limit,$this-> setting['list_focus'],$doctype);
                $departstr=$this->multi($count, $this->setting['list_focus'], $page,$url);
                $this->view->assign('navtitle',$navtitle);
                $this->view->assign("departstr",$departstr);
                $this->view->assign('type',$type);
                $this->view->assign('list',$list);
                //$this->view->display('list');
                $_ENV['block']->view('list');
        }

Entonces, podemos construir $doctype=2 o 1=1 para evitar la detección del interruptor, lo que provoca la inyección.

4. Referencias

Supongo que te gusta

Origin blog.csdn.net/qq_45590334/article/details/126285755
Recomendado
Clasificación