从0到1 CTFer成功之路》任意文件读取漏洞---学习笔记
时间:2023-02-25 13:00:00
1.3 读取任何文件的漏洞
所谓文件读取漏洞,就是攻击者可以通过某种方式读取服务器上开发者不允许读取的文件。从整个攻击过程来看,往往是收集资产信息的有力补充手段。攻击者在这个漏洞触发点窥探服务器的各种配置文件、存储在文件形式中的密钥、服务器信息(包括正在执行的流程信息)、历史命令、网络信息、应用源代码和二进制程序。
读取文件的漏洞往往意味着被攻击者的服务器即将被攻击者完全控制。当然,如果服务器严格按照标准安全规范部署,攻击者很难获得有价值的信息,即使应用程序中存在可用文件读取漏洞。每个可部署的文件读取漏洞Web几乎所有的应用程序语言都存在。当然,这里的存在本质上不是语言本身的问题,而是开发人员在开发过程中考虑不足造成的疏漏。
业界公认的代码库通常被称为轮子如果轮子中有漏洞,在程序员反复迭代轮子代码的同时,漏洞也会逐级传递。随着底层轮子代码的不断引用,轮子代码中存在的安全隐患对于处于呼叫链顶端的开发者来说几乎是透明的。对于挖掘Web对于应用框架漏洞的安全人员来说,耐心对这条调用链的逆向追溯也是一个非常严峻的挑战。此外,开发者无法通过代码控制任何文件读取的漏洞。这种情况下的漏洞通常是由Web Server由于自身问题或服务器配置不安全。Web Server运行的基本机制是从服务器中读取代码或资源文件,然后将代码文件传输给解释器或CGI程序执行,然后将执行结果和资源文件反馈给客户端用户,许多存在于其中的文件操作可能会受到攻击者的干预,导致非预期阅读文件和错误地将代码文件视为资源文件。
1.3.1文件读取漏洞常用触发点
1.3.1.1 WEB语言
1. PHP
PHP不再详细介绍标准函数中相关文件的阅读部分,包括但可能不限于:file_get_contents()、file()、fopen()函数(及其文件指针操作函数fread()、fgets()等),包含与文件相关的函数(include()、require()、include_once()、require_once()等),以及通过PHP阅读文件的执行系统命令(system()、exec()等)。这些函数在PHP因此,它在整个应用中非常常见PHP在代码审计过程中,审计人员将重点关注这些函数
PHP开发技术越来越倾向于单入口、多层次、多通道模式,涉及PHP文件之间的呼叫密集而频繁。为了写出一个高度重用的文件呼叫函数,开发人员需要将一些动态信息传输到函数(如可变文件名)(见图1-3-1)
public static function registerComposerLoader($composerPath) {
if (is_file(scomposerPath . ' autoload_namespaces. php')) {
smap = require $composerPath. ' autoload_namespaces. php'; oreach (smap as snamespace => $path) {
self::addPsro(snamespace, $path); } } if (is_file($composerPath . ' autoload_psr4. php')) {
smap = require $composerPath. ' autoload_psr4. php'; oreach (smap as snamespace => $path) {
self: : addPsr4(snamespace, spath); if (is_file(scomposerPath . ' autoload_classmap. php')) {
sclassMap = require $composerPath. ' autoload_classmap. php'; if (sclassMap) {
self::addClassMap(sclassMap); } } } }
PHP扩展也提供了一些可以读取文件的函数,例如,php-curl扩展(文件内容作为HTTP body)涉及文件存取的库(如数据库相关扩展、图片相关扩展)、XML模块造成的XXE等。
在遇到的有关PHP文件包含的实际问题中,我们可能遇到三种情况:
①文件路径前面可控,后面不可控;
②文件路径后面可控,前面不可控;
③文件路径中间可控。
2.Python
Python的Web应用更多地倾向于通过其自身的模块启动服务,同时搭配中间件、代理服务将整个Web应用呈现给用户,用户和Web应用交互的过程本身就包含对服务器资源文件的请求,所以容易出现非预期读取文件的情况。
3.Java
除了Java本身的文件读取函数FileInputStream、XXE导致的文件读取,Java的一些模块也支持“file://”协议,这是Java应用中出现任意文件读取最多的地方,如Spring Cloud ConfigServer路径穿越与任意文件读取漏洞(CVE-2019-3799)、Jenkins任意文件读取漏洞(CVE-2018-1999002)等
4.Ruby
Ruby的任意文件读取漏洞通常与Rails框架相关。到目前为止,我们已知的通用漏洞为Ruby On Rails远程代码执行漏洞(CVE-2016-0752)、Ruby On Rails路径穿越与任意文件读取漏洞(CVE-2018-3760)、Ruby On Rails路径穿越与任意文件读取漏洞(CVE-2019-5418)
5.Node
已知Node.js的express模块曾存在任意文件读取漏洞(CVE-2017-14849),但笔者还未遇到相关CTF赛题。CTF中Node的文件读取漏洞通常为模板注入、代码注入等情况。
1.3.1.2 中间件/服务器相关
1.Nginx错误配置
Nginx一般被视为Python-Web反向代理的最佳实现。然而它的配置文件如果配置错误,就容易造成严重问题。
Location /static {
alias /home/myapp/static/;
}
很可能是运维或者开发人员想让用户可以访问static目录(一般是静态资源目录)。但是,如果用户请求的Web路径是/static…/,拼接到alias上就变成了/home/myapp/static/…/,此时便会产生目录穿越漏洞,并且穿越到了myapp目录
注意:漏洞的成因是location最后没有加“/”限制,Nginx匹配到路径static后,把其后面的内容拼接到alias,如果传入的是/static…/,Nginx并不认为这是跨目录,而是把它当作整个目录名,所以不会对它进行跨目录相关处理。
2.数据库
MySQL的load_file()函数可以进行文件读取,但是load_file()函数读取文件首先需要数据库配置FILE权限(数据库root用户一般都有),其次需要执行load_file()函数的MySQL用户/用户组对于目标文件具有可读权限(很多配置文件都是所有组/用户可读),主流Linux系统还需要Apparmor配置目录白名单(默认白名单限制在MySQL相关的目录下),可谓“一波三折”。即使这么严格的利用条件,我们还是经常可以遇到相关的文件读漏洞。
3.软链接
bash命令ln-s可以创建一个指向指定文件的软链接文件,然后将这个软链接文件上传至服务器,当我们再次请求访问这个链接文件时,实际上是请求在服务端它指向的文件。
4.FFmpeg
2017年6月,FFmpeg被爆出存在任意文件读取漏洞
5.Docker-API
Docker-API可以控制Docker的行为,一般来说,Docker-API通过UNIX Socket通信,也可以通过HTTP直接通信。当我们遇见SSRF漏洞时,尤其是可以通过SSRF漏洞进行UNIX Socket通信的时候,就可以通过操纵Docker-API把本地文件载入Docker新容器进行读取(利用Docker的ADD、COPY操作),从而形成一种另类的任意文件读取。
1.3.1.3 客户端相关
1.浏览器/Flash XSS
一般来说,很多浏览器会禁止JavaScript代码读取本地文件的相关操作,如请求一个远程网站,如果它的JavaScript代码中使用了File协议读取客户的本地文件,那么此时会由于同源策略导致读取失败。但在浏览器的发展过程中存在着一些操作可以绕过这些措施,如Safari浏览器在2017年8月被爆出存在一个客户端的本地文件读取漏洞。
2.MarkDown语法解析器XSS
与XSS相似,Markdown解析器也具有一定的解析JavaScript的能力。但是这些解析器大多没有像浏览器一样对本地文件读取的操作进行限制,很少有与同源策略类似的防护措施
1.3.2 文件读取漏洞常见读取路径
1.3.2.1 Linux
1.flag名称(相对路径)
有时fuzz一下flag名称便可以得到答案。注意以下文件名和后缀名
2.服务器信息(绝对路径)
(1)/etc目录/etc目录下多是各种应用或系统配置文件,所以其下的文件是进行文件读取的首要目标。(2)/etc/passwd/etc/passwd文件是Linux系统保存用户信息及其工作目录的文件,权限是所有用户/组可读,一般被用作Linux系统下文件读取漏洞存在性判断的基准。读到这个文件我们就可以知道系统存在哪些用户、他们所属的组是什么、工作目录是什么。(3)/etc/shadow/etc/shadow是Linux系统保存用户信息及(可能存在)密码(hash)的文件,权限是root用户可读写、shadow组可读。所以一般情况下,这个文件是不可读的。(4)/etc/apache2//etc/apache2/是Apache配置文件,可以获知Web目录、服务端口等信息。CTF有些题目需要参赛者确认Web路径。(5)/etc/nginx//etc/nginx/是Nginx配置文件(Ubuntu等系统),可以获知Web目录、服务端口等信息。(6)/etc/apparmor(.d)/(6)/etc/apparmor(.d)//etc/apparmor(.d)/是Apparmor配置文件,可以获知各应用系统调用的白名单、黑名单。例如,通过读配置文件查看MySQL是否禁止了系统调用,从而确定是否可以使用UDF(User Defined Functions)执行系统命令。(7)/etc/(cron.d/|crontab)/etc/(cron.d/|crontab)是定时任务文件。有些CTF题目会设置一些定时任务,读取这些配置文件就可以发现隐藏的目录或其他文件。(8)/etc/environment/etc/environment是环境变量配置文件之一。环境变量可能存在大量目录信息的泄露,甚至可能出现secret key泄露的情况。(9)/etc/hostname/etc/hostname表示主机名。(10)/etc/hosts/etc/hosts是主机名查询静态表,包含指定域名解析IP的成对信息。通过这个文件,参赛者可以探测网卡信息和内网IP/域名。(11)/etc/issue/etc/issue指明系统版本。(12)/etc/mysql//etc/mysql/是MySQL配置文件。(13)/etc/php//etc/php/*是PHP配置文件。(14)/proc目录/proc目录通常存储着进程动态运行的各种信息,本质上是一种虚拟目录。注意:如果查看非当前进程的信息,pid是可以进行暴力破解的,如果要查看当前进程,只需/proc/self/代替/proc/[pid]/即可。(15)其他目录Nginx配置文件可能存在其他路径:
/usr/local/nginx/conf/*(源代码安装或其他一些系统)
日志文件:
/var/log/*
(经常出现Apache2的Web应用可读/var/log/apache2/access.log从而分析日志,盗取其他选手的解题步骤)
Apache默认Web根目录:
/var/www/html/
PHP session目录:
/var/lib/php(5)/sessions/(泄露用户session)
用户目录:
[user_dir-you_know]/.bash-history
《泄露历史执行命令)
[user_dir-you_know]/.bashro
(部分环境变量)
[user_dir-you-know]/.ssh/id-rsa(.pub)
(ssh登录私钥/公钥)
[user_dir-you_know]/.viminfo
(vim使用记录)
[pid]指向进程所对应的可执行文件。有时我们想读取当前应用的可执行文件再进行分析,但在实际利用时可能存在一些安全措施阻止我们去读可执行文件,这时可以尝试读取/proc/self/exe。例如:
/proc/[pid]/fa/(112.)
/proc/[pid]/maps
/proc/[pid]/(mountsImountinfo).
/proc/[pid]/net/*
(读取[pid]指向进程的stdout或stderror或其他)
([pid]指向进程的内存映射)
[pid]指向进程所在的文件系统挂载情况.CTF常见的是Docker环境这时mounts会泄露一些敏感路径)
([pid]指向进程的网络信息,如读取TCP将获取进程所绑定的TCP端口ARP将泄露同网段内网IP信息)
1.3.3 文件读取漏洞实例
根据大量相关CTF真题的整理,本节介绍文件读取漏洞的实战,希望参赛者在阅读后仔细总结,熟练掌握,对日后解题会有很大帮助。
1.3.3.1 兵者多诡(HCTF 2016)
【题目简介】在home.php中存在一处include函数导致的文件包含漏洞,传至include函数的路径参数前半部分攻击者可控,后半部分内容确定,不可控部分是后缀的.php。
$fp = empty($_GET['fp'])?'fail':$_GET['fp'];
if(preg_match('/\.\./',$fp))
{
die('No NO NO!');
}
if(preg_match('/rm/i',$_SERVER["QUERY_STRING"]))
{
die();
}
if($fp !== 'fail')
{
if(!(include($fp.'.php')))
{
}
}
在upload.php处存在文件上传功能,但上传至服务器的文件名不可控。
//function. php
function create_imagekey(){
return shal($_SERVER['REMOTE-ADDR'].$_SERVER[' HTTP-USER-AGENT'].time().mt-rand());
...
//upload. php
$imagekey = create_imagekey();
move_uploaded_file($name, "uploads/$imagekey.png");
echo "";
...
解决思路:
发现首页只有一个上传表单,先上传一个正常文件进行测试。通过对上传的数据进行抓包,发现POST的数据传输到了“?fp=upload”,接着跟随数据跳转,会发现结果跳转到“?fp=show&imagekey=xxx”
经验程度不同的参赛者的思考方向会产生差异:
- 第一步
新手: 可以先测试上传功能
有经验的:看到fp参数,会联想到file pointer,即fp的值可能与文件相关。 - 第二步接下来的差异会在第一步的基础上继续扩大。
新手:这个文件上传的防护机制到底该怎样绕过?
有经验者:直接访问show.php、upload.php,或者想办法寻找文件中名含有show、upload等特殊含义的PHP文件,或者把show/upload改成其他已知文件"home"。
更有经验的参赛者:将fp参数的内容改为“./show”“…/html/show”等。我们无法得知文件包含的目标文件具体路径是什么,如果是一个很奇怪的路径,就无法找到其原始PHP文件,这时“./show”形式能很好地解决这个困难,进而轻松地判断这里是否存在任意文件包含漏洞。 - 第三步新手:这道题一定需要0day才能绕过防护,我可以放弃了。有经验的参赛者:
根据直接访问“show.php/upload.php"和 "?fp=home"的结果,判断这里是一个include文件包含。利用Filter机制,构造形如“php://filter/convert.base64-encode/resource=xxx” 的攻击数据读取文件,拿到各种文件的源码;利用 zip://协议,搭配上传的Zip文件,包含一个压缩的Webshell文件;再通过zip://协议调用压缩包中的Webshell,访问这个Webshell的链接为
?fp=zip://uploads/fe5elc43e6e6bcfd506f0307e8ed6ec7ecc3821d.png%231&shell=phpinfo(); fe5e1c43e6e6bcfd506f0307e8ed6ec7ecc3821d.png (zipfile)-1. php (phpfile) => “ php eval($_GET[' shell ']);?>”
【总结】
- 题目首先考查了选手对于黑盒测试任意文件读取/包含漏洞的能力,每个人都有自己独有的测试思路,上面所写的思路仅供参考。在进行黑盒测试时,我们要善于捕获参数中的关键词,并且具有一定的联想能力。
- 考查了参赛者对Filter的利用,如php://filter/convert.Base64-encode(将文件流通过Base64进行编码)。③考查了选手对zip://协议的利用:将文件流视为一个Zip文件流,同时通过“#”(%23)选出压缩包内指定文件的文件流。读者可能不太理解第③点,下面具体说明。我们上传一个Zip文件至服务器,当通过zip://协议解析这个压缩文件时,会自动将这个Zip文件按照压缩时的文件结构进行解析,然后通过“#(对应URL编码%23)+文件名”的方式对Zip内部所压缩的文件进行索引(如上面的例子就是内部存储了个名为1.php的文件)。这时整个文件流被定位到1.php的文件流,所以include实际包含的内容是1.php的内容,具体解析流程见图1-3-3。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RmGgreyz-1632131824084)(/uploads/l1215484545/images/m_aea429d493b82d779b5a5564c90a3cd4_r.png)]
1.3.3.2 PWNHUB-Classroom
实例简介:使用Django框架开发,并通过不安全的方式配置静态资源目录。
# urls.py
from django.conf.urls import url
from . import views
urlpatterns = [ url('"$', views . IndexView. as-view(), name=' index'), url('^login/$', views . LoginView. as_view), name=' login')
url('^logout/$', views. LogoutView. as_view(), name=' Logout'),
url('"static/(? Pspath>.*)', views. StaticFilesView. as_view(), name=' static')]
...
## views. py
class StaticFilesView(generic. View):
content_type = ' text/plain'
def get(self, request, * args, ** kwargs):
filename = self. kwargs [' path']
filename =os.path.join(settings.BASE_DIR, 'students', 'static', filename)
name, ext = os.path.splitext(filename)
if ext in ('.py', '.conf', '.sqlite3', '.yml'):
raise exceptions. PermissionDenied(Permission deny')
try:
return HttpResponse(Filewrapper(open(filename, ' rb'), 8192),
content_type=self. content_type)
except BaseException as e: raise Http4o4(' static file not found')
....
【题目难度】中等。
【知识点】Python(Django)静态资源逻辑配置错误导致的文件读取漏洞;Pyc字节码文件反编译;Django框架ORM注入。
【解题思路】第一个漏洞:代码先匹配到用户传入的URL路径static/后的内容,再将这个内容传入os.path.join,与一些系统内定的目录拼接后形成一个绝对路径,然后进行后缀名检查,通过检查,该绝对路径将传入open()函数,读取文件内容并返回用户。
第二个漏洞:views.py的类LoginView中。可以看到,将用户传入的JSON数据加载后,加载得到的数据直接被代入了x.objects.filter(Django ORM原生函数)
class LoginView(JsonResponseMixin, generic. TemplateView):
template_name = ' login. html
def post(self, request, * args, ** kwargs):
data =json. Loads(request. body. decode())
stu = models. Student. objects. filter(** data).first()
if not stu or stu. passkey != data[' passkey']:
return self._jsondata(", 403)
else:
request. session[' is_login'] = True
return self.. jsondata("', 200)
...
先打开题目,看到HTTP返回头部中显示的Server信息:
Server: gunicorn/19.6.0 Django/1.10.3 CPython/3.5.2
我们可以得知题目是使用Python的Django框架开发的,当遇到Python题目没有给源码的情况时,可以第一时间尝试是否存在目录穿越相关的漏洞(可能是Nginx不安全配置或Python框架静态资源目录不安全配置),这里使用“/etc/passwd”作为文件读取的探针,请求的路径为:
/static/../../../../../../etc/passwd
可以发现任意文件读取漏洞的确存在,但在随后尝试读取Python源代码文件时发现禁用了几个常见的后缀名,包括Python后缀名、配置文件后缀名、Sqlite后缀名、YML文件后缀名:
if ext in ('. py', '. conf', '. sqlite3', '. yml'):
raise exceptions.PermissionDenied(' Permission deny')
在Python 3中运行Python文件时,对于运行的模块会进行缓存,并存放在__pycache__目录下,其中pyc字节码文件的命名规则为:
pycache/views.cpython-34.pyc是一个文件名的示例。这里其实考查的是对Python的了解和Django目录结构的认知。将请求的文件路径更换为符合上面规则的路径:
/static/../__pycache__/urls. cpython-35.pyc
成功地读取了PYC字节码文件。继续读取所有剩余的PYC文件,再反编译PYC字节码文件获取源代码。通过对获得的源码进行审计,我们发现存在ORM注入漏洞,继续利用该注入漏洞便可得到flag内容,见图1-3-4。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MXTnreH6-1632131824086)(/uploads/l1215484545/images/m_ad450e01d3f736e9f6a59d42aa6c36d9_r.png)]
【总结】1. 参赛者要通过HTTP头中的指纹信息判断题目的相关环境。当然,这里可能涉及一些经验和技巧,需要通过大量的实践积累。2. 熟悉题目所用的环境和Web应用框架。即使参赛者刚开始时不熟悉,也要快速搭建并学习该环境、框架的特性,或者翻看查阅手册。注意:快速搭建环境并学习特性是CTF参赛者进行Web比赛的基本素养。3. 黑盒测试出目录穿越漏洞,进而进行任意文件读取。4. 源代码审计,根据2所述,了解框架特性后,通过ORM注入获得flag。
1.3.3.3 Show me the shell I(TCTF/0CTF 2018 Final)
【题目简介】题目的漏洞很明显,UpdateHead方法就是更新头像功能,用户传入的URL的协议可以为File协议,进而在Download方法中触发URL组件的任意文件读取漏洞。
//UserController. class
@ RequestMapping(value=("/headimg. do"}.
method={
org.Springframework.web.bind.annotation.RequestMethod.GET))
public void UpdateHead(@ RequestParam("url") string url)
{
String downloadPath = this.request.getSession().getServletContext().getRealPath("/")+"/headimg/";
String headurl ="/headimg/"+ HttpReq.Download(url, downloadPath);
User user = (User) this.session.getAttribute("user");
Integer uid = user.getId();
this.userMapper.UpdateHeadurl(headurl, uid);
//HttpReq. class
...
public static String Download(String urlString, String path)
{
String filename = "default.jpg";
if (endwithImg(urlstring)) {
try URL url = new URL(urlstring);
URLConnection urlConnection = url.openConnection();
urlConnection. setReadTimeout (5000): int size = urlConnection. getContentLength(); if(size < 10240)
InputStream is = urlConnection. getInputStream();
...
【知识点】Java URL组件通过File协议列出目录结构,进而读取文件内容。
【解题思路】对Java class字节码文件进行反编译(JD);通过代码审计,发现源码中存在的漏洞
【总结】参赛者要积累一定的经验,了解URL组件可使用的协议,赛后分享见图1-3-5。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dm1Y5KPf-1632131824088)(/uploads/l1215484545/images/m_246686925cef5f6ecd5c907afc2f3159_r.png)]
1.3.3.4 BabyIntranet I(SCTF 2018)
【题目简介】本题采用了Rails框架进行开发,存在Ruby On Rails远程代码执行漏洞(CVE-2016-0752),可以被任意读取文件(该漏洞其实质是动态文件渲染)。
def show
render params [: template]
end
通过读取源码发现,该应用程序使用了Rails的Cookie-Serialize模块,通过读取应用的密钥,构造恶意反序列化数据,进而执行恶意代码。
#config/initializers/cookies_serializer.rb
Rails.application.config.action_dispatch.cookies_serializer =:json
题目难度】中等。
【知识点】Ruby On Rails框架任意文件读取漏洞;Rails cookies反序列化。
【解题思路】对应用进行指纹探测,通过指纹信息发现是通过Rails框架开发的应用,接着可以在HTML源码中发现链接/layouts/c3JjX21w,对软链接后面的部分进行Base64解码,发现内容是src_ip。查阅Rails有关漏洞发现动态模板渲染漏洞(CVE-2016-0752),将…/…/…/…/…/…/etc/passwd编码成Base64放在layouts后,成功返回/etc/passwd文件的内容。尝试渲染日志文件(…/log/development.log)直接进行代码执行失败,发现没权限渲染这个文件,接着读取所有可读的代码或配置文件,发现使用了cookies_serializer模块。尝试读取当前用户环境变量发现没权限,于是尝试读取/proc/self/environ,获取到密钥后,使用metasploit中相应的Ruby反序列化攻击模块直接攻击。
【总结】①通过Ruby On Rails远程代码执行漏洞(CVE-2016-0752)进行任意文件读取(出题人对漏洞代码进行了一定程度的修改,使用了Base64编码),见图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VGIU8yhn-1632131824090)(/uploads/l1215484545/images/m_43e3705c2c6eda2917b9f525356f3ecf_r.png)]
②服务器禁止了Log日志的读取权限,因此不能直接通过渲染日志完成getshell。通过读取源码,我们可以发现应用中使用了Rails的Cookie-Serialize模块。整个模块的处理机制是将真正的session_data序列化后通过AES-CBC模式加密,再用Base64编码2次,处理流程见图1-3-7。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-azEVeYAM-1632131824091)(/uploads/l1215484545/images/m_8ed99d6be41ceaf390381971b5e1f608_r.png)]
从服务器返回的Set-Cookie也能印证这一点,见图1-3-8。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OnNtaFeT-1632131824092)(/uploads/l1215484545/images/m_34dfbc4c95822099b8c97d6dae32ef77_r.png)]
我们可以通过任意文件读取漏洞获取/proc/self/environ的环境变量,找到AES加密所使用的secret_key,接着借助secret_key伪造序列化数据。这样,当服务端反序列化时,就会触发漏洞执行恶意代码,见图1-3-9。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kZ3G88wY-1632131824093)(/uploads/l1215484545/images/m_bd3ffd1d8ec2466d7d959d171c2872c0_r.png)]
1.3.3.5 SimpleVN(BCTF 2018)
【题目简介】题目的功能主要分为如下两点。(1)用户可以设置一个模板用来被渲染,但是这个模板设置有一定的限制,只能使用“.”和字母、数字。另外,渲染模板的功能只允许127.0.0.1(本地)请求。
const checkPUG = (upug) => {
const fileterkeys = [' global', ' require']
return /^[a-zA-ze-9\.]*$/g. test(upug) && ! fileterkeys. some(t => upug.toLowerCase(). includes(t))
console. Log(' Generator pug template')
const uid = req. session. user. uid const body = #$(upug}
console. Log ' body', body)
const upugPath= path. join(' users', utils. md5(uid), '$(uid). pug')
console. Log ' upugPath', upugPath)
try {
fs. writeFileSync(path. resolve(config. VIEWS_PATH, upugPath), body)
catch (err)
{
}
(2)题目中存在一个代理请求的服务,用户输入URL并提交,后端会启动Chrome浏览器去请求这个URL,并把请求页面截图,反馈给用户。当然,用户提交的URL也有一定限制,必须是本地配置的HOST(127.0.0.1)。这里存在一个问题,就是我们传入File协议的URL中的HOST部分是空的,所以也可以绕过这个检查。
const checkURL = (shooturl) => {
const myURL = new URL(shooturl)
return config. SERVER_HOST. incLudes(myURL. host)
【题目难度】中等。
【知识点】浏览器协议支持及view-source的利用;Node模板注入;HTTPRequest Header:Range。【
解题思路】通过审计源码,发现模板注入漏洞和服务端浏览器请求规则,同时找到了解题方向:获取flag的路径,并读取flag的内容。
const FLAG-PATH = path. resolve(constant. ROOT-PATH, '*)) const FLAGFILENAME = process. env. FLAGFILENAME II '**
通过模板注入process.env.FLAGFILENAME获取flag文件名,获取整个Node应用所在目录process.env.PWD,使用view-source:输出被解析成HTML标签的结果,见图1-3-10。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RWbTNRxH-1632131824094)(/uploads/l1215484545/images/m_e45ad823400fb014de31924d8469d054_r.png)]
使用file://+绝对路径读取config.js中的FLAG_PATH,见图1-3-11。读取flag内容,使用HTTP请求头的Range来控制输出的开始字节和结束字节。题目中的flag文件内容很多,直接请求无法输出真正flag的部分,需要从中间截断开始输出,见图1-3-12。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8JEgmdZf-1632131824095)(/uploads/l1215484545/images/m_d1e2e6a2b1225c91aead6e60b4db54b0_r.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W2xdbZNP-1632131824095)(/uploads/l1215484545/images/m_37b47ce02f0833c49089c365cc3644ca_r.png)]
【总结】①题目中的任意文件读取其实与Node并无太大关系,实质上是利用浏览器支持的协议,属于比较新颖的题目。②读取文件的原则是按需读取而不盲目读取,盲目读取文件内容会浪费时间。③同样使用浏览器特性有关的题目还有同场比赛的SEAFARING2,通过SSRF漏洞攻击selenium server,控制浏览器请求file:///读取本地文件。读者如果感兴趣可以搜寻这道题。
1.3.3.6 Translate(Google CTF 2018)
【题目简介】根据题目返回的{ {userQuery}},我们容易想到试一下模板注入,使用数学表达式{ {3*3}}进行测试。
"in_Lang-query_is_spelled": "In french, <b>((userQuery}}b> is spelled
b>.".
通过{ {this. p a r e n t . parent. parent.parent.window.angular.module(‘demo’)._invokeQueue[3][2][1]}}读取部分代码,发现使用了i18n.template渲染模板,通过i18n.template(’./flag.txt’)读取flag。
($compile, $sce, i