1. 程式人生 > >安恆9月賽部分復現記錄

安恆9月賽部分復現記錄

前言

趕緊趁著電腦的螢幕修好了,剛好安恆的web題目有復現,趕緊做。。。。。又從這幾個題目裡面學習到新知識了小結一下

正文

web

babybypass

這個題目我記得我當初做的時候是一直考慮著用$以及_去繞過這些數字字母之類的東西,突然發現他這個題目裡面把那個$以及_也經過了過濾,這樣的話就少了很多可能性,而且這題比原題的限制長度小了,更有難度。現在就開始總結一下從這個題目學到的知識點
1.php短標籤輸出,這個知識點很久之前就已經見過。
<?=?>這個就是短標籤,相當於<?php echo …;?>一般在php的配置檔案php.ini

中有一個short_open_tag的值,開啟以後可以使用PHP的短標籤:<? ?>,但是在現實開發中一般不推崇這種做法。。
但這個題目就很巧妙用了這個東西
2.第二個就是關於php函式eval函式的一些東西,這裡的php執行函式是eval,在php官方手冊上面有這樣一句話,這是一個坑了,要想執行程式碼的話,我們就需要先用?>去閉合,把上一段程式碼就給結束離開 ,然後我們才可以利用<?=?>去輸出
ilI9II.md.png

區別一下有些系統執行函式比如system(有回顯),exec(沒回顯),以及反引號這類的,這是直接執行系統函式的,一會我們也會用到
ilIPit.png
ilIiJP.png

3.Linux萬用字元的作用

 *        代表『 0 個或無窮多個』任意字元
 ?        代表『一定有一個』任意字元

4.Linux 下面一切皆檔案
就是說我們平常一般在下面Linux下面的輸入的命令,都可以通過檔案去同樣的執行,這對我來說是新知識。。。。。
ilIMiq.png

然後我們就可以去實現payload,這個題目是Apache搭的,我們可以去預設目錄先檢視一波檔案,一般來說預設目錄在/var/www/html

構造code=?><?=`/???/??? /???/???/????/*`;?>相當於code=?><?=` /bin/cat /var/www/html/*`;?>

然後會發現php程式碼裡面的原始碼getflag那一部分的函式是是從/flag裡面讀取的。。。。聽說這題的getflag函式也是個坑。。好像不能直接從url裡面呼叫

所以再來構造一下
code=?><?=`/???/??? /????`;?>相當於code=?><?=` /bin/cat /flag`;?>得到flag
ilIQJ0.md.png

神奇的CMS

進入網站發現有使用者登入,然後測試發現弱密碼
admin admin123

然後點選幾個模組發現有兩個模組有點奇怪
ilIlWV.md.png

這兩個模組一個模組有提示,另一個模組存在輸入並且存在回顯
先看提示,一是讓你下載原始碼包,另外是提示flag在哪,先把原始碼包下載下來,原始碼裡面有兩個控制器,一個是content,另一個是site

發現是Yii框架的東西,幸虧我還是做過Yii框架的開發的,對此還是有點小熟悉。。。

這裡看回放的時候還是學到點東西的,就是我們在程式碼審計的時候一般要找有輸入有輸出的地方,這裡很可能就存在這漏洞,這就相當於在腦裡面建了一個模型,但是想想也很正常,平常我們學程式設計的時候也沒過多的去考慮程式設計的輸入輸出,比如我們在一開始用C程式設計的時候你會忘掉除數不為0的前提嗎?所以說我們更多的是考慮程式碼的核心功能能否實現。

所以在ADD_IMG(因為有輸入有回顯)頁面裡面就很有可能存在漏洞了,所以我們得專注程式碼裡面描述這一段東西的內容

Yii框架講究的是MVC架構,一般程式碼審計的話得先找Controller裡面的東西畢竟都是些邏輯性的東西,關鍵的功能也在裡面
所以在這個url裡面的site是指的控制器,而backup就是site控制器裡面的操作
ilI8QU.png
我以前的部落格講過
ilIdF1.md.png

從程式碼裡面你會發現site控制器裡面都是些展示頁面以及登陸的功能,但是不要過分以為登入裡面會有sql注入,畢竟Yii框架裡面是可以通過對login函式設定rules再來通過validate去檢測是否出現SQL注入,所以出現sql注入機率不大

但是在回放裡面學到了一種更騷氣的程式碼除錯的方法。。就是當你不懂框架的時候如何將其變成普通的php程式碼進行除錯
這個方法就是將跟框架有關的東西去掉把關於框架的類看成一個普通的類,再把跟業務邏輯以及資料庫相關操作的功能有關的去掉,然後新建那個類,單純測試一下那個輸入輸出的函式就好

嘗試除錯,我這裡用的phpstorm去除錯,在parstIf函式下了斷點,發現執行到下圖所示的地方就會重新返回到echo輸出語句中
ilIrQO.md.png
說明要在字串裡面加上{if此類的字串
隨便找一個地方加加,為的是能夠進入else的迴圈
ilIbwj.md.png
進入else會發現到buildregx又會有一個新的函式,其實這個函式就是為了構建一個正則表示式,在字串前後新增/,可以從debug的結果看得出來
ilIqTs.md.png
這時候我們就需要匹配/{if:(.*?)}(.*?){end if}/is這樣的正則,但是之前的輸入會導致其跳出該迴圈,從而又直接返回了結果,不能進入到for迴圈裡面執行eval函式
ilIXYq.md.png
這時候就又需要構建對應的東西了,我們把正則裡面的(.*?)改為自己隨意的內容就好
ilIjf0.md.png
然後再追蹤一次變化過程,經過preg_match_all這個函式的時候我們就可以看到這個函式會把原來的匹配的字元分成三段,首先是iar[0][0]是匹配到的字串{if:2333}23333{end if},然後是分別是第一個任意內容以及第二個任意內容匹正則配到的字串,iar[1][0]=2333,iar[2][0]=23333

繼續走的話,這時候就可以進行一次迴圈了,因為現在arlen=1,然後我們就可以繼續執行下去了,後買的事情就很簡單了,先是判斷iar[1][0]以及iar[2][0]裡面是否有等號,再判斷一下iar[2][0]裡面是否有{elseif以及else,這幾個判斷都不影響
ilopXF.md.png
最後還是將iar[1][0]放在這個eval語句裡面執行
iloCm4.md.png
梳理一下邏輯也就是說檢查最少的是iar[1][0],只檢查他是否存在=,那就好辦了,在eval語句中我們只需要對iar[1][0]閉合一下語句我們就可以執行我們想要的命令即可

Payload
{if:1)print_r(`cat /tmp/flag`);die();//}123{end if}第一種思路直接在裡面拼接命令執行語句即可
iloi79.png

第二種思路就是在沒有過濾$GOLBALS全域性變數的前提下使用拼接,但個人感覺這東西會在PHP開發中給禁掉吧畢竟可能會對程式碼裡面的其他變數造成影響,這個套路一般都是用來寫過waf的小馬用的

{if:1)$GLOBALS['_G'.'ET'][a]($GLOBALS['_G'.'ET'][b]);die();//}{end if}

嘗試這個的是時候還以為伺服器壞了。。。突然發現processing request。。。應該是沒傳參,傳參了就好了

iloAt1.md.png

iloM0H.md.png
iloQ7d.md.png

Crypto

簡單加密

這個題目幫我複習了一下資訊保安數學基礎第一章的內容,所以我就記錄一下這個題目,其他題目就不寫了

這個關鍵點就是作取餘運算的時候結果是不會超過餘數的,如果有打過ACM的人就會知道有一個叫快速冪的東西跟這個也差不多,極大地降低了程式碼所需要的空間度,使得數不會越界

觀察主函式現在我們需要的就是passwd這個引數,但是從generate_passwd是得到最多passwd不會超過0xB18E,所以我們就可以選擇爆破
ilo3tI.md.png

跑一下指令碼就可以得到flag

#!/usr/bin/env python
# -*- coding:utf-8 -*- 
from Crypto.Cipher import AES
from Crypto import Random


def encrypt(data, password):
    bs = AES.block_size
    pad = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
    iv = "0102030405060708"
    cipher = AES.new(password, AES.MODE_CBC, iv)
    data = cipher.encrypt(pad(data))
    return data
 
def decrypt(data, password):
    unpad = lambda s : s[0:-ord(s[-1])]
    iv = "0102030405060708"
    cipher = AES.new(password, AES.MODE_CBC, iv)
    data  = cipher.decrypt(data)
    return unpad(data)
    
def generate_passwd(key,result):
    data_halt = "LvR7GrlG0A4WIMBrUwTFoA==".decode("base64")
    rand_int =  int(decrypt(data_halt, key).encode("hex"),16)
    #round = 0x7DC59612
    result = result * (rand_int % 0xB18E) % 0xB18E
    return encrypt(str(result), key)
    

if __name__ == '__main__':

    key = '17abeca4cc4c432a52c2b7f6d24d1888'
    
    output = "u6WHK2bnAsvTP/lPagu7c/K3la0mrveKrXryBPF/LKFE2HYgRNLGzr1J1yObUapw"

    for result in range(0xB18E):
        passwd = generate_passwd(key.decode("hex"),result)
        r = decrypt(output.decode("base64"), passwd)
        if 'flag' in r:
            print r