Git資訊洩漏原理探究
記錄一下Git的基礎操作原理,還有Git資訊洩漏在比賽中的一些問題。
Git基本操作
git init
建立一個git_test資料夾,並使用git init命令初始化一個空的Git 倉庫
Rain@Rai4over MINGW64 ~/Desktop $ mkdir git_test Rain@Rai4over MINGW64 ~/Desktop $ cd git_test/ Rain@Rai4over MINGW64 ~/Desktop/git_test $ git init Initialized empty Git repository in C:/Users/Rain/Desktop/git_test/.git/ Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ ls -a ./../.git/
git add && git commit
建立一個test1.txt檔案,通過git add跟蹤這個檔案,然後使用git commit提交更新到倉庫。
Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ echo 'test1file haha' >test1.txt Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ cat test1.txt test1file haha Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) test1.txt nothing added to commit but untracked files present (use "git add" to track) Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git add test1.txt warning: LF will be replaced by CRLF in test1.txt. The file will have its original line endings in your working directory. Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file:test1.txt Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git commit -m "test1file" [master (root-commit) 4089fd9] test1file 1 file changed, 1 insertion(+) create mode 100644 test1.txt Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git log commit 4089fd99a9bd7ee0cec8f94ce1c66b650054080e (HEAD -> master) Author: Rai4over <[email protected]> Date:Sun Sep 2 17:52:35 2018 +0800 test1file
檢視狀態 && 暫存區
git status命令可以檢視倉庫狀態,列出當前目錄所有還沒有被git管理追蹤的檔案和被git管理且被修改但還未提交更新的檔案.。
檔案狀態
倉庫中的檔案可能存在於這三種狀態:
- Untracked files → 檔案未被跟蹤;
- Changes to be committed → 檔案已暫存,這是下次提交的內容;
- Changes not staged for commit → 已跟蹤檔案的內容發生了變化,但還沒有放到暫存區。
通過git add的檔案會加入暫存區,之後git commit會將暫存區的所有檔案提交更新。
Git物件和目錄
Git物件
在Git系統中有四中型別的物件,所有的Git操作都是基於這四種類型的物件。
-
"blob",這種物件用來儲存檔案的內容。
-
"tree",可以理解成一個物件關係樹,它管理一些"tree"和“blob”物件。
-
"commit",指向一個"tree",它用來標記專案某一個特定時間點的狀態。它包括以下關於時間點的元資料,如時間戳、最近一次提交的作者、指向上次提交、
-
"tag",給某個提交增添一個標記。
SHA1雜湊值
在Git系統中,每個Git物件都通過雜湊值來代表這個物件。雜湊值是通過SHA1演算法計算出來的,長度為40個字元(40-digit)。
Commit 物件
commit物件一般包含以下資訊:
-
代表commit的雜湊值
-
指向tree物件的雜湊值
-
作者
-
提交者
-
註釋

Tree 物件
Tree物件一般包含以下資訊:
-
代表blog的雜湊值
-
指向tree物件的雜湊值

Blob 物件
Blob物件一般包含用來儲存檔案的內容。

Tag 物件
標籤物件一般包含以下資訊:
- 一個物件名(SHA1簽名)
- 物件型別
物件間的關係
commit、tree、blob三個物件的簡單關係如下:
若倉庫的目錄結構如下:
|-- README
`-- lib
|-- inc
| `-- tricks.rb
`-- mylib.rb
git物件 結 構的關係圖如下:
.git目錄
在我們使用git init初始化git倉庫的時候,會生成一個.git的隱藏目錄,git會將所有的檔案,目錄,提交等轉化為git物件,壓縮儲存在這個資料夾當中。
-
COMMIT_EDITMSG:儲存最新的commit message,Git系統不會用到這個檔案,只是給使用者一個參考
-
config:這個是GIt倉庫的配置檔案
-
description:倉庫的描述資訊,主要給gitweb等git託管系統使用
-
HEAD:這個檔案包含了一個檔期分支(branch)的引用,通過這個檔案Git可以得到下一次commit的parent
-
hooks:這個目錄存放一些shell指令碼,可以設定特定的git命令後觸發相應的指令碼;在搭建gitweb系統或其他
git託管系統會經常用到hook script
-
index:這個檔案就是我們前面提到的暫存區(stage),是一個二進位制檔案
-
info:包含倉庫的一些資訊
-
logs:儲存所有更新的引用記錄
-
objects:所有的Git物件都會存放在這個目錄中,物件的SHA1雜湊值的前兩位是資料夾名稱,後38位作為物件檔名
-
refs:這個目錄一般包括三個子資料夾,heads、remotes和tags,heads中的檔案標識了專案中的各個分支指向的當前commit
-
ORIG_HEAD:HEAD指標的前一個狀態
Git引用
Git中的引用是一個非常重要的概念,對於理解分支(branch)、HEAD指標以及reflog非常有幫助。
Git系統中的分支名、遠端分支名、tag等都是指向某個commit的引用。比如master分支,origin/master遠端分支,命名為V1.0.0.0的tag等都是引用,它們通過該儲存某個commit的SHA1雜湊值指向某個commit
HEAD也是一個引用,一般情況下間接指向你當前所在的分支的最新的commit上。HEAD跟Git中一般的引用不同,它並不包含某個commit的SHA1雜湊值,而是包含當前所在的分支,所有HEAD直接執行當前所在的分支,然後間接指向當前所在分支的最新提交。
檢視當前分支,當前分支為master,並通過git log可以發現這個commit就是master分支上最新的提交。
Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git status On branch master nothing to commit, working tree clean Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ cat .git/HEAD ref: refs/heads/master Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ cat .git/refs/heads/master 29bb92d948b861d13c0912694a6000d33b62fea3 Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git cat-file -p 29bb92d948b861d13c0912694a6000d33b62fea3 tree 57f5feedc4fa5a298d6d89aa93fd205bb588458e parent 4089fd99a9bd7ee0cec8f94ce1c66b650054080e author Rai4over <[email protected]> 1535977002 +0800 committer Rai4over <[email protected]> 1535977002 +0800 test2 Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git log -n 1 commit 29bb92d948b861d13c0912694a6000d33b62fea3 (HEAD -> master, test) Author: Rai4over <[email protected]> Date:Mon Sep 3 20:16:42 2018 +0800 test2
所有的內容都是環環相扣的,我們通過HEAD找到一個當前分支,然後通過當前分支的引用找到最新的commit,然後通過commit可以找到整個物件關係模型:
引用和分支
-
git branch test,建立名為test分支。
-
git checkout test,切換到test分支。
除了master分支,建立了一個test的分支,切換到新分支,檢視HEAD,HEAD的指向發生了變化。
再次檢視“.git/refs/heads/”目錄,可以看到除了master檔案之外,又多了一個test檔案,檢視該檔案的內容也是一個雜湊值。
Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ cat .git/HEAD ref: refs/heads/master Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ cat .git/refs/heads/master 29bb92d948b861d13c0912694a6000d33b62fea3 Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git checkout test Switched to branch 'test' Rain@Rai4over MINGW64 ~/Desktop/git_test (test) $ cat .git/HEAD ref: refs/heads/test Rain@Rai4over MINGW64 ~/Desktop/git_test (test) $cat .git/refs/heads/test 29bb92d948b861d13c0912694a6000d33b62fea3
-
git show-ref --heads,命令就可以檢視所有的頭
Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git show-ref --heads 29bb92d948b861d13c0912694a6000d33b62fea3 refs/heads/master 29bb92d948b861d13c0912694a6000d33b62fea3 refs/heads/test
進入.git/logs資料夾,可以看到這個資料夾也有一個HEAD檔案和refs目錄,這些就是記錄倉庫修改的地方。
目錄下記錄了包括git commit,git checkout,git stash等命令的操作歷史。
-
git stash,儲存當前工作進度,會把暫存區和工作區的改動儲存起來。
執行完這個命令後,在執行git status命令,就會發現當前是一個乾淨的工作區,沒有任何改動。
當然可以使用git stash pop來恢復之前的進度.
Rain@Rai4over MINGW64 ~/Desktop/git_test/.git/logs (GIT_DIR!) $ ls HEADrefs/ Rain@Rai4over MINGW64 ~/Desktop/git_test/.git/logs (GIT_DIR!) $ ls refs/ heads/stash Rain@Rai4over MINGW64 ~/Desktop/git_test/.git/logs (GIT_DIR!) $ ls refs/heads/ mastertest
索引index
index/stage,就是更新的暫存區,看看index檔案。
index(索引)是一個存放了已排序的路徑的二進位制檔案,並且每個路徑都對應一個SHA1雜湊值。在Git系統中,可以通過git ls-files --stage來顯示index檔案的內容。
建立新檔案,新增到暫存區(SHA1雜湊值已經改變)
$ git ls-files --stage 100644 1453b33002ce30e42459ca3ea867c2aeefe1a0fa 0test1.txt 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0test2.txt Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ echo 333 > test3.txt Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git ls-files --stage 100644 1453b33002ce30e42459ca3ea867c2aeefe1a0fa 0test1.txt 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0test2.txt Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git add test3.txt warning: LF will be replaced by CRLF in test3.txt. The file will have its original line endings in your working directory. Rain@Rai4over MINGW64 ~/Desktop/git_test (master) $ git ls-files --stage 100644 1453b33002ce30e42459ca3ea867c2aeefe1a0fa 0test1.txt 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0test2.txt 100644 55bd0ac4c42e46cd751eb7405e12a35e61425550 0test3.txt
物件的儲存
所有的Git物件都會存放在.git/objects目錄中,物件SHA1雜湊值的前兩位是資料夾名稱,後38位作為物件檔名。
.git洩漏 | Example
我們常用的.git洩漏檔案恢復工具有很多,但是如果不懂原理,可能會遇到恢復檔案缺少的問題,因為多數工具只會恢復HEAD指標的commit物件。
這裡拿某比賽的題目舉個例子:
開啟瀏覽器console發現提示,並且恢復HEAD指標的commit物件並不能得出正確原始碼。
獲得commit
這裡可以想到使用git stash,當然最穩妥的辦法當然是遍歷所有commit,不管是通過log,refs,HEAD等檔案或目錄均可,得到的commit指標都是一樣的。
-
commit hash ,e5b2a2443c2b6d395d06960123142bc91123148c
-
訪問commit物件,.git/objects/e5/b2a2443c2b6d395d06960123142bc91123148c
獲得tree
-
根據commit物件得到tree物件的hash
-
訪問tree物件,.git/objects/76/9905f5a6f425ce62ed9a1cbf375a61fb56b406
獲得Blob
-
根據tree物件得到blob物件的hash
-
訪問blob物件,.git/objects/8e/f569f235780f24c42b60f50d528a03f7238c80
其實只要明白了原理,不管commit的hash藏在哪裡都能應對自如。