CTF特训营:技术详解、解题方法与竞赛技巧
上QQ阅读APP看书,第一时间看更新

6.1 源码泄露

CTF比赛中经常会出现需要源码审计的题目,源码有时候会直接提供给你,有时候则需要自己去找,因此下面为大家列出几种常用的源码泄露的途径及利用技巧。

1.常见备份文件

在实战中,备份文件一般是由于维护人员的疏忽,忘记删除而留在服务器中的文件。这时攻击者就能够通过枚举常见备份文件名来得到关键代码,从而进行源代码的审计。为了能够找到这些备份文件,我们可以使用一些敏感文件扫描工具来进行探测,这类工具比较多,这里就不逐一介绍了。一般常见备份文件有以下两种类型。

(1)文本备份文件

技术人员在Linux系统下会使用诸如vim或gedit等文本编辑器,当编辑器崩溃或因异常退出时会自动备份当前文件;有时候程序开发者在编写代码时,也可能会将实现某功能后的代码备份后再进行后续开发工作。下面以index.php为例列出一些可能的备份文件:


.index.php.swp
.index.php.swo
index.php~
index.php.bak
index.php.txt
index.php.old
...

(2)整站源码备份文件

有时候题目会将整站源码打包,然后放在网站的根目录下,这时,只要找到这个压缩包就能开始进行源码审计了。下面列出一些常见的整站备份文件名,举例如下:


www
wwwdata
wwwroot
web
webroot
backup
dist
...

后面再加上各种压缩文件后缀名,举例如下:


.zip
.tar
.tar.gz
.7z
.rar
...

有时候,还可以利用其他可能会泄露目录结构或文件名的敏感文件来获取备份文件的位置,如“.DS_Store”等。

2.Git泄露

大家对GitHub一定都不陌生,这上面不仅可以找到各种好用的开源工具,而且可以上传一些自己开发的项目,是我们获取源码的一个途径。

(1)通过特征搜索

当某个网站存在某个明显特征字符串的时候,就有可能通过GitHub的搜索功能来搜索到该项目。下面的例子是NJCTF 2017的题目chall,进入题目提供的登录界面后可以看到一个非常显眼的字符串,如图6-1所示。

图6-1 题目界面

通过搜索“请登录,bibibibibibibibibibibibibibi~”就能够得到源码,注意一定要登录GitHub后再进行搜索,搜索结果如图6-2所示。

(2)通过.git泄露

我们知道每个git项目的根目录下都存在一个.git文件夹,这个文件夹的作用就是存储项目的相关信息,这里笔者推荐的工具是GitHack和scrabble,下面就来结合git原理将scrabble源码简要分析一遍。

图6-2 GitHub搜索结果

了解git原理之前,我们首先应在本地建立一个git工程并初始化,然后再commit一次,如图6-3所示。

图6-3 初始化git工程

然后,进入.git目录下,看看目录中有什么文件,如图6-4所示。

图6-4 git目录结构

这里列举几个比较关键的文件。

·HEAD:标记当前git在哪个分支中。

·refs:标记该项目里的每个分支指向的commit。

·objects:git本地仓库存储的所有对象。

而git的对象有如下四个。

·commit:标记一个项目的一次提交记录。

·tree:标记一个项目的目录或者子目录。

·blob:标记一个项目的文件。

·tag:命名一次提交。

所以,我们可以通过下面的几个操作找到项目的每个文件夹及文件,首先是确定commit对象,如图6-5所示。

图6-5 确定commit对象

其中,第三条命令最后的参数只需要输入第二条命令返回结果的前6位即可,然后我们就能查看里面的tree对象和blob对象了,如图6-6所示。

图6-6 查看对象

这样就可以看到之前commit的三个文件了,由于这三个文件是空的,所以blob标识是相同的。由于样例文件为空,所以最后一条读blob数据的命令返回为空,但在实际情况下一般不会这样。

在实战过程中,根据这个原理,可以将当前项目完全还原下来。

接下来,我们来分析一下scrabble的源码,以便更进一步了解从git目录恢复文件的原理(https://github.com/denny0223/scrabble)。

首先是输入存在“.git”目录中的url,接着就是查看HEAD文件获取分支的位置,然后得到分支的hash值,代码如下:


domain=$1
ref=$(curl -s $domain/.git/HEAD | awk '{print $2}')
tmp_dir=`echo $domain | awk -F'[/:]' '{print $4}'`
mkdir $tmp_dir
cd $tmp_dir
lastHash=$(curl -s $domain/.git/$ref)

得到hash值后首先本地初始化一个git,接着通过parseCommit获取全部对象,最后使用reset重设分支,这样就将项目重新建立在本地了,代码如下:


git init
cd .git/objects/
parseCommit $lastHash
cd ../../
echo $lastHash > .git/refs/heads/master
git reset --hard

接下来,我们来看看三个自定义函数:parseCommit、parseTree、downloadBlob。

1)parseCommit函数用于下载commit对象,同时会将其parent也一并下载下来,代码如下:


function parseCommit {
    echo parseCommit $1
    downloadBlob $1
    tree=$(git cat-file -p $1| sed -n '1p' | awk '{print $2}')
    parseTree $tree
    parent=$(git cat-file -p $1 | sed -n '2p' | awk '{print $2}')
    [ ${#parent} -eq 40 ] && parseCommit $parent
}

2)parseTree函数用于下载tree对象,同时列出tree下的所有对象,分类为tree或者blob后处理,代码如下:


function parseTree {
    echo parseTree $1
    downloadBlob $1
    while read line
    do
    type=$(echo $line | awk '{print $2}')
    hash=$(echo $line | awk '{print $3}')
    [ "$type" = "tree" ] && parseTree $hash || downloadBlob $hash
    done < <(git cat-file -p $1)
}

3)downloadBlob函数用于将与hash对应的文件下载下来:


function downloadBlob {
    echo downloadBlob $1
    mkdir -p ${1:0:2}
    cd $_
    wget -q -nc $domain/.git/objects/${1:0:2}/${1:2}
    cd ..
}

理解了git目录结构和原理之后,再遇到与git相关的题目时就能够自如地应对题目的变化了,我们需要找到的其实就是下载项目对应commit的hash。

XDCTF 2015的Web2就是git泄露的实例。该例题中的“.git”文件夹下留着的项目只有README.md和“.gitignore”,README.md告诉我们“All source files are in git tag 1.0”,所以我们只需要找到tag=1.0的hash即可,直接找到refs/tags/1.0并读取hash,然后在代码上将lastHash的值修改为我们得到的hash值,还原出来后就能得到flag了。

3.svn泄露

svn与git类似,同样是项目初始化时会生成一个“.svn”目录,所以也可以用工具来解决,笔者这里推荐使用svn-extractor,内容与git差不多,所以不再赘述。

4.利用漏洞泄露

如果能发现任意文件包含漏洞或者任意文件存在下载漏洞,就有可能下载到题目的源码,对其进行审计。

任意文件包含和下载的漏洞的表现形式包含但不限于以下几种:

·http://example.com/download.php?file=abc.pdf

·http://example.com/show_image.php?file=1.jpg

·http://example.com/read.aspx?file=./upload/1.txt

将file参数修改为“../index.php”这种形式,就可以利用漏洞下载源代码文件了,通常比赛题目中这种漏洞是与别的漏洞配合起来使用的,如XSS和SSRF漏洞。