0x00 前言
之前参加护网杯,因为太菜,没做几个题。可能是WP发的早,WP竟然广为流传,是我第一篇阅读量突破3000+的博客,还是感谢队友们(道萝岗特森、WCatalpa.T)的强力输出,实在诚惶诚恐。想着,以后也要更加努力的去学习啊,毕竟这么菜,被叫大佬有点不好意思==。先从护网杯的几道没做出的题开始,来学习吧。
0x01 Easy_dump
比赛时,这个题用了很多的还原工具,花了很多时间,但是都没做出来。。。
后来发现还原工具都是假的==、disk genius
提出来的图片是空数据。
这是一个取证分析题,很久以前做过类似的取证分析,但是很久没做过了,有些忘记了。
volatility -f easy_dump.img imageinfo |
volatility提供很多查看当时系统状态信息的指令,我们先用pslist查看当时的进程
volatility -f mem.vmem --profile=WinXPSP2x86 pslist |
root@kali:~/Desktop# volatility -f easy_dump.img --profile=Win7SP1x64 pslist |
受篇幅制约,这里只列举部分进程,可以看到有常见的NotePad.exe
,explorer.exe
,还有一个可疑的进程DumpIt
尝试查看NotePad中是否有什么隐藏信息,volatility中notepad插件,可以很方便的查看notepad进程的记录:
volatility notepad -f easy_dump.img pslist --profile=Win7SP0x64 |
但是没发现什么有用的提示之类的东西。
再尝试从其他程序入手。volatility中iehistoty插件,可以很方便的查看ie浏览器的浏览记录。
Volatility Foundation Volatility Framework 2.6 |
提示指向了一个文件phos.jpg
,可能是有东西的,于是利用filescan
命令找到文件位置。
root@kali:~/Desktop# volatility -f ./easy_dump.img --profile=Win7SP1x64 filescan | grep phos.jpg |
记下偏移0x0000000023e067e0
,使用dumpfiles把这张图片dump出来。
命令:
volatility -f ./easy_dump.img --profile=Win7SP1x64 dumpfiles -Q 0x0000000023e067e0 --name -D ./res |
root@kali:~/Desktop# volatility -f ./easy_dump.img --profile=Win7SP1x64 dumpfiles -Q 0x0000000023e067e0 --name -D ./res |
打开文件,没有直接发现flag,猜测下面是图片隐写了。
binwalk -e file.None.0xfffffa800a488e10.phos.jpg.vacb |
得到一个压缩包(伪加密,解压就是.img)和一个.img
镜像
strings查看.img文件,发现字符串yispyweih!uokt_jwyx_snh_gzfne
和一堆二位数字对,怀疑是类似坐标之类的:
strings message.img |
通过数量(256*256)推测这个数据代表二维码上的黑点和白点 脚本如下:
from PIL import Image,ImageDraw |
得到二维码
扫码得到hint:
Here is the vigenere key: aeolus, but i deleted the encrypted message。
之前的密文是维吉尼亚密码,key是aeolus
在线解密得到flag
flag:yeeeeeeet!just_find_and_solve |
0x02 fez
这个题一开始思路错了,后来是道萝岗特森大佬发现出题人的思路,顺利输出。
我的思路:
因为test是已知的,第一次fez的调用中用到了test和K,并且密文也是已知的,那么能不能解出来K,然后去解flag?于是回到round函数,可以看到最后一次加密结束时,用到的是K7,那么最后一次的E_L和E_R我们是知道的,我们能不能推出前一次的L和R,也就是没有经过轮加密的L和R,我们已经知道下一轮的密文L等于上一轮的R,下一轮的R等于上一轮的R异或L再异或当前轮所使用的K,那么很容易得出我们能获得上一轮所使用的明文的右半部分,有了上一轮的右半部分,加密后的新的右半部分,如果我们求出上一轮的左边的部分,就能求出当前轮所使用的K,但是根绝目前已知的所有条件,上一轮的明文的左半部分是无法求出的,卡在这里了很长时间,结果无奈放弃了
dlgts大哥的思路:
正向来模拟加密的过程,来发现是否存在漏洞,在round函数中,新一轮的左半部分等于上一轮的右半部分,下一轮的右半部分等于上一轮的左右两部分异或之后再和当前轮的K异或,产生的新一轮的左右两部分拼接成为新的密文。
知识点:
相同的数异或为0
0异或任何数还是其本身
于是开始模拟:
第一轮:R+R^L^K1 |
结果由K变量和初始的明文的两部分组成
又因为在包含flag的m变量中进行了相同的fez函数加密,所以和test调用fez函数以后的结果形式是相同的,只是初始的明文不同,又因为根据异或运算的计算逻辑,相同的数异或为0,0异或任何数还是其本身,所以即使我们不知道K变量是什么,因为两次运算结果的K盒子在结果中的形式相同,所以可以抵消掉。
即由于test变量是用同样的算法得到的,所以test的最终结果为
Rtest^K2^K3^K5^k6 + Rtest^Ltest^K1^K3^K4^K6^K7 【TestRes】 |
而我们已知test
和TestRes
,即Ltest
和Rtest
,
于是在进行推导,容易得到flag的两个部分。
TestRes[0:27] ^ res[0:27] == R ^ Rtest |
MyExp:
import os |
题目反思
- 对于随机数的解密处理:
- 伪随机,并非真正的随机,了解其seed和原理,很大情况下,可以爆破
- 对于其加密原理进行数学模拟和分析,很可能在某些步骤就可以抵消而和随机结果无关。就像很多高中数学题一样。
- 妄图通过加密步骤逆向解决,对于有随机的题目来说四徒劳的。
- 对于加密而言,数学尤其重要,很多数学的思想可以借鉴。
- 异或是很特殊的运算,A^A = 0,0 ^ any = any.
0x03 easy_web
3.1 环境复现
之前很少接触JavaWeb,最近倒是在学习这门专业选修也是搞得懵懵的。
因为原题环境关闭了,这里利用了一位大佬写的环境进行测试和学习(感谢0rz)。
https://github.com/iBearcat/FastJson-JdbcRowSetImpl-RCE
关于ubantu服务器搭建tomcat环境我写了另一篇博客,需要的朋友欢迎移步阅读。
配置环境不多说
root@V0W:# export tomcat=/usr/local/tomcat/tomcat7.0 |
访问url,看到和比赛时相似的环境
之前只接触了一点JavaWeb,这里也是看了很久各位大佬的文档(在文末参考链接中都给出了),才算是对其利用有了初步了解,加上利用大佬们现成的Poc,解决了这个题目。
首先是原理篇:大佬们的解释一定比我好多了,大家可以参考文末的链接,进行理解。
Fastjson可以将java的对象转换成json的形式,也可以用来将json转换成java对象,效率较高,被广泛的用在web服务以及android上,它的JSONString()方法可以将java的对象转换成json格式,同样通过parseObject方法可以将json数据转换成java的对象。
fastjson漏洞出现的地方也就是JSON.parseObject这个方法上面。
在反序列化成对象的过程中,能通过类初始化时候的构造函数或者变量的setter方法执行恶意代码。但是由于这个恶意构造的类不可控,所以这种攻击看上去是无法实施的。
但是可以将编译好的.class或者.jar文件转换成byte[],然后通过defineClass加载byte[]返回class对象。即将类的定义转换成bcel编码进行攻击。
3.2 大佬的攻击思路
- 构造一个payload——evil.class 能够直接执行shell,或者下载一个预先存在VPS某端口的用于反弹shell的py文件
- 写另一个类,用于将evil.class 生成bcel编码
- 构造包含恶意攻击类bcel编码的JSON数据,用于攻击
- VPS上监听一个端口,实施攻击,反弹shell
3.3 利用大佬的payload测试攻击
evil.java:
public class evil extends Thread { |
编译得到.class文件
javac evil.class |
另一个BCELencode.java
文件中,进行bcel编码
import com.sun.org.apache.bcel.internal.classfile.Utility; |
得到BCEL编码
然后构造JSON poc
{ |
将evil.java的cmd修改为 python /tmp/poc.py
再操作一遍
就可以执行py文件,反弹一个shell,进行命令执行。
0x04 Easy_laravel
4.1 环境复现
首先感谢4uuu大佬出了这么好的题还给出复现环境
git clone https://github.com/sco4x0/huwangbei2018_easy_laravel |
4.2 源码发现
在登录等页面查看源码,可以得到
<!-- https://github.com/qqqqqqvq/easy_laravel --> |
得到代码,之后composer install 。代码就完整了。开始审计。
首先看路由route文件夹
Route::get('/', function () { return view('welcome'); }); |
也可以在目录下通过php artisan route:list
命令查看,更加直接,详细
4.3 sql注入
几乎所有操作都需要进行登录,而且 UploadController
和 FlagController
都使用了一个叫做 admin
的中间件,在 app/Http/Middleware/AdminMiddleware.php
中可以看到代码
public function handle($request, Closure $next) |
需要当前用户的邮箱为 admin@qvq.im
,这时发现使用这个邮箱是注册不上的,因为系统已经内置了这个管理员账号。但是由于不知道密码,我们也没办法利用。
接下来看看,有没有什么办法可以得到管理员邮箱的密码。在\app\Http\Controllers\NoteController.php
中,发现一个明显的SQL注入(类似二次注入,注入点在注册页面):
public function index(Note $note) |
确实也可以注入,本以为可以通过注入得到admin用户的密码,那就简单了,不是吗?
结果,最终发现密码是随机加密的,无法破解。
$factory->define(App\User::class, function (Faker\Generator $faker) { |
4.4 密码重置
既然SQL注入不行,那么就得想其他方法才行。在上一步SQL注入的测试中,发现\database\migrations\2014_10_12_100000_create_password_resets_table.php
是一个重置密码的文件,并且其中代码是这样写的。
public function up() |
也就是只要有邮箱和token,我们就可以修改密码,问题在于token怎么弄到手呢?
这里用到Laravel5.4的一个特性 :
重置密码时,如果存在这个用户的话,有个生成token的操作,之后将生成的token直接存在了数据库中。
利用注册页面的SQL注入
admin' union select 1,(select token from password_resets limit 0,1),3,4,5# |
拿到token后,访问链接
http://120.79.180.214/password/reset/1c9d0f377a75dd48abaa90dd7fa4eb35653da39561d6f9c33bdb14a8a0849616(token值) |
4.5 登陆后台
发现有4个功能:upload,files,flag,note
但是直接访问会发现页面提示 no flag
,这里页面内容不一致,在 laravel 中,模板文件是存放在 resources/views
中的,然后会被编译放到 storage/framework/views
中,而编译后的文件存在过期的判断。
加上题目的提示:
- blade expired
- pop chain
可以发现是blade过期的问题
发现Blade 是 laravel 提供的一个简单强大的模板引擎。它不像其他流行的 PHP 模板引擎那样限制你在视图中使用原生的 PHP 代码,事实上它就是把 Blade 视图编译成原生的 PHP 代码并缓存起来。缓存会在 Blade 视图改变时而改变,这意味着 Blade 并没有给你的应用添加编译的负担。
所以我们这的思路很清晰:
- 因为旧的缓存存在,导致我们看不到flag
- 我们可以利用pop chain删掉缓存文件
- 读到flag
那么缓存文件在哪里呢?我们查看源码发现
在 Illuminate/View/Compilers/Compiler.php
中可以看到
public function isExpired($path) |
结合相关文档和提示nginx是坠吼的 ( 好麻烦,默认配置也是坠吼的
那么很容易得知web目录
/usr/share/nginx/html |
blade默认缓存是
/storage/framework/views |
因此,使用了nginx的默认配置,那么flag文件的完整路径就是
/usr/share/nginx/html/resources/views/auth/flag.blade.php |
经过sha1后得到 34e41df0934a75437873264cd28e2d835bc38772.php
4.6 利用反序列化删除文件
过期时间是依据文件的最后修改时间来判断的,所以判断服务器上编译后的文件最后修改时间大于原本模板文件,那么怎么去删除(修改)编译后的文件?
这里发现composer.json
中提供了大量组件,我们安装一下,然后全局搜索,容易发现有unlink()
4.7 phar神来之笔
那最后怎么触发序列化呢?这里用到了我们BlackHat会议演讲的phar://方法
参考这篇文章我校表哥seaii的利用 phar 拓展 php 反序列化漏洞攻击面
check在\app\Http\Controllers\UploadController.php
public function check(Request $request) |
是会使用file_exists的,并且path和filename可控!
出题师傅的payload
<?php |
然后上传1.gif,check的时候传入自定义的path和filename,然后访问/flag ,得到flag
4.8 本题总结
由于之前没太接触过Laravel框架,最近看了很多资料,才算勉强看懂了大佬们的WP和思路。不得不说,这道题目非常有水平,考察了二次注入,Laravel框架的特性和逻辑漏洞,POP链与反序列化, phar协议扩展反序列化的攻击等。学习了0rz
0x05 后记
本次护网杯,收获甚大,出题人的水平非常高,出的题很有意思,而且学到很多东西, 感谢所有出题人0rz。
当然,也发现自身的很多不足,比如对一些web框架不熟悉,对Javaweb很不熟悉,杂项有些东西还没吃透,后期需要更加努力的去弥补这些了。
还有就是,很多东西喜欢拖,拖到现在才做完护网杯的复习和学习==、
最后,有点可惜,没有进入决赛,问了一下排名90,感觉再做出一个题赢就差不多能进,哎(;´д`)ゞ
。打算下周开始接触逆向和pwn,希望看到文章的逆向大佬给个逆向入门的学习路线吧,欢迎联系我(联系方式见关于页面)。