赞
踩
目录
相信不少打过ctf的师傅对githack这个词并不陌生,它实际上是利用git源码泄露漏洞的工具名称。Git 源码泄露漏指的的是由于配置不当,客户端可以通过 http/https 协议直接访问服务器本地 .git 文件夹中的内容。而我们知道,.git文件夹是 Git(版本控制工具)存储代码信息的文件夹,这意味着我们的源码可能会通过该文件夹泄露出去。
在此之前,师傅们可以先做一个小实验,即将一个代码仓库的 .git 文件夹单独复制出来到另外的一个文件夹中,看看会发生什么?
可以看到,执行git status后依然能看到被删除的文件,这时候只需要简单地执行git checkout -- .恢复最后一次commit的提交时的状态,被删除的文件也就恢复了,这实际上证明,假如能下载 .git 文件夹,就相当于下载了源码。
知道了上述这点之后,我们关注的重点变成了如何下载 .git 文件夹。一个最简单的情况是该目录由于中间件( apache / nginx) 配置不当导致其能直接目录遍历,这时候只需要通过 wget -r或者编写脚本递归遍历下载文件夹和文件即可。
假如不能目录遍历,那么我们就需要先了解Git内部原理,才能够进行接下来的工作。(Git内部原理章节的内容参考 Pro Git第二版)
首先要明白的是, Git 本质上是一个现代化的版本控制系统,而我们常用的 git 命令则是操纵这个系统的命令行工具。
当我们通过git init命令创建一个新的存储库时,其 .git 目录如下所示:
- Plaintext
- $ ls -F1
- HEAD
- config*
- description
- hooks/
- info/
- objects/
- refs/
需要重点关注的是HEAD 文件、(还未创建的)index 文件,和 objects 目录、refs 目录。这些文件或目录是 git 的核心组成部分,其中HEAD文件保存了当前所在分支,index文件保存了暂存区信息,objects目录存储了所有数据内容,refs目录则存储了指向数据的指针。
数据对象(blob object)
Git是一个内容寻址文件系统,这意味着 Git 的核心部分是一个简单的键值对数据库,我们可以简单地往数据库中插入任意类型的内容,数据库会返回一个键值,通过该键值可以在任意时刻再次检索该内容。
我们可以使用git的底层子命令:hash-object来理解上述这段话。首先,我们要确定objects目录为空:
- Plaintext
- $ git init test
- Initialized empty Git repository in /tmp/test/.git/
- $ cd test
- $ find .git/objects
接下来,我们往数据库中存储一段内容:
- Plaintext
- $ echo 'test content' | git hash-object -w --stdin
- d670460b4b4aece5915caf5c68d12f560a9fe3e4
-w 选项指示 hash-object 命令存储数据对象;若不指定此选项,则该命令仅返回对应的键值。--stdin 选项则指示该命令从标准输入读取内容;若不指定此选项,则须在命令尾部给出待存储文件的路径。该命令会返回一段 SHA-1 哈希值作为键值,现在我们再来看看 .git/objects 文件夹:
- Plaintext
- $ find .git/objects -type f
- .git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
现在可以在 objects 目录下看到一个文件。这就是 Git 存储内容的方式—— 一个文件对应一条内容,以计算出来的 SHA-1 哈希值为文件命名。校验和的前两个字符用于命名子目录,余下的 38 个字符则用作文件名。
使用git的底层子命令:cat-file可以读取刚刚存储的内容,为 cat-file 指定 -p 选项可指示该命令自动判断内容的类型,并为我们显示格式友好的内容:
- Plaintext
- $ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
- test content
树对象(tree object)
接下来要讨论的对象类型是树对象,它能解决文件名保存的问题,也允许我们将多个文件组织到一起。Git 以一种类似于 UNIX 文件系统的方式存储内容,所有内容均以树对象和数据对象的形式存储,其中树对象对应了 UNIX 中的目录项,数据对象则大致上对应了 inodes 或文件内容。一个树对象包含了一条或多条树对象记录,每条记录含有一个指向数据对象或者子树对象的 SHA-1 指针,以及相应的模式、类型、文件名信息,一个示例图如下:
为了创建一个树对象,我们首先需要通过暂存一些文件来创建暂存区,可以使用 git 的底层子命令:update-index来完成:
- Plaintext
- $ git update-index --add --cacheinfo 100644,d670460b4b4aece5915caf5c68d12f560a9fe3e4,test.txt
--add选项是必须的,因为此前该数据并不存在于暂存区中,该选项会将该数据加入暂存区。--cacheinfo也是必须的,这是因为我们将一段数据而非普通文件加入暂存区。其后跟着以逗号分隔的三个参数,分别为文件模式(这里为100644,即普通文件),SHA-1 键值与想要保存为的文件名。
执行完该命令后不会有任何输出,接下来我们再使用 git 的底层子命令:write-tree来创建一个树对象。
- Plaintext
- $ git write-tree
- 80865964295ae2f11d27383e5f9c0b58a8ef21da
为了验证其是一个树对象,可以使用cat-file:
- Plaintext
- $ git cat-file -p 80865964295ae2f11d27383e5f9c0b58a8ef21da
- 100644 blob d670460b4b4aece5915caf5c68d12f560a9fe3e4 test.txt
最后我们再使用status来查看一下当前git状态:
- Plaintext
- $ git status
- On branch master
-
- No commits yet
-
- Changes to be committed:
- (use "git rm --cached <file>..." to unstage)
- new file: test.txt
-
- Changes not staged for commit:
- (use "git add/rm <file>..." to update what will be committed)
- (use "git restore <file>..." to discard changes in working directory)
- deleted: test.txt
可以看到我们已经将数据放入了暂存区中,由于当前文件夹中并没有 test.txt 这个文件,所以其显示为 deleted 。
提交对象(commit object)
提交对象解决了以下问题:假如我们存在多个树对象,而每个树对象则有一个独立的键值,我们不可能去记忆所有的键值。
所以我们将树对象提交为提交对象,在其上附加上作者信息/作者邮箱以及一段注释。
使用 git 的底层子命令:write-tree来从一个树对象中创建提交对象。
- Plaintext
- $ echo 'first commit' | git commit-tree 80865964295ae2f11d27383e5f9c0b58a8ef21da
- 97147cdc4915da7d51e98cd99cea5705e5e98045
- $ git cat-file -p 97147cdc4915da7d51e98cd99cea5705e5e98045
- tree 80865964295ae2f11d27383e5f9c0b58a8ef21da
- author WAY29 41830147+WAY29@users.noreply.github.com 1698912061 +0800
- committer WAY29 41830147+WAY29@users.noreply.github.com 1698912061 +0800
- first commit
实际上,在正常使用 git 时,当前提交对象还会指向前一个提交对象(第一个提交对象则不会指向任何提交对象),以此形成一个链表,一个提交对象的示例图如下:
除了上述提交的几个对象之外,实际上git还存在tag和repack对象,在此不再做介绍。
在了解了 Git 内部原理之后,我们就可以开始来编写 Githack 了,在Yaklang中实现的 Githack 使用了 go-git 这个依赖库。其执行流程如下:
我们以 ctfhub-技能树-Web-信息泄露-Git泄露-Log 题目为例,以此展示如何在 Yaklang 中使用 Githack ,启动题目后,我们获取靶场地址,这里的靶场地址是:
http://challenge-9615288119a7b693.sandbox.ctfhub.com:10800/
此时我们打开Yakit,执行以下代码:
- Plaintext
- err = git.GitHack("http://challenge-9615288119a7b693.sandbox.ctfhub.com:10800/", "C:\\Users\\xxx\\Desktop\\git\\gittest")
- // 第一个参数是存在 Git 源码泄露的 URL 地址,第二个参数是要存储的目标文件夹
- // 后续可以接收多个选项参数,例如 git.threads(10) 指定线程数,git.useLocalGitBinary(true) 指定使用本地环境变量中的git命令进行git fsck进行兜底
- // git.httpOpts(poc.redirectTimes(10), poc.https(true)) 指定http请求时请求选项参数
- die(err)
执行完毕后,若代码没有出现报错,则证明代码已经恢复成功,我们来查看一下 Git 仓库情况:
可以看到,我们已经成功恢复出 Git 仓库的日志,根据日志我们知道,最新的一次commit已经把 flag 删除了,所以我们需要切换到的第二个 commit ,即add flag这个 commit ,之后我们直接 ls 即可看到 flag ,读取即可。
结合上面的案例,我们可以知道在 Yaklang 中调用 Githack 是一件非常简单的事情,也十分欢迎广大师傅们更新最新 Yaklang 版本,对这个新功能进行试用与反馈。
渗透工具
技术文档、书籍
面试题
帮助你在面试中脱颖而出
视频
基础到进阶
环境搭建、HTML,PHP,MySQL基础学习,信息收集,SQL注入,XSS,CSRF,暴力破解等等
应急响应笔记
学习路线
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。