Assert.AreEqual("NO", sub.ReadString());
}
+ [TestMethod]
+ public void ReadStringProtectsAgainstOverrun()
+ {
+ const string TestDataFromAPreviousPacket = "You shouldn't be able to see this data";
+
+ // An extra byte from the length of TestData when written via MessageWriter
+ int DataLength = TestDataFromAPreviousPacket.Length + 1;
+
+ // THE BUG
+ //
+ // No bound checks. When the server wants to read a string from
+ // an offset, it reads the packed int at that offset, treats it
+ // as a length and then proceeds to read the data that comes after
+ // it without any bound checks. This can be chained with something
+ // else to create an infoleak.
+
+ MessageWriter writer = MessageWriter.Get(SendOption.None);
+
+ // This will be our malicious "string length"
+ writer.WritePacked(DataLength);
+
+ // This is data from a "previous packet"
+ writer.Write(TestDataFromAPreviousPacket);
+
+ byte[] testData = writer.ToByteArray(includeHeader: false);
+
+ // One extra byte for the MessageWriter header, one more for the malicious data
+ Assert.AreEqual(DataLength + 1, testData.Length);
+
+ var dut = MessageReader.Get(testData);
+
+ // If Length is short by even a byte, ReadString should obey that.
+ dut.Length--;
+
+ try
+ {
+ dut.ReadString();
+ Assert.Fail("ReadString is expected to throw");
+ }
+ catch (InvalidDataException) { }
+ }
+
[TestMethod]
public void GetLittleEndian()
{
using System;
+using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
public int Length;
public int Offset;
+ public int BytesRemaining => this.Length - this.Position;
+
public int Position
{
get { return this._position; }
public string ReadString()
{
int len = this.ReadPackedInt32();
+ if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}");
+
string output = UTF8Encoding.UTF8.GetString(this.Buffer, this.readHead, len);
this.Position += len;
public byte[] ReadBytesAndSize()
{
int len = this.ReadPackedInt32();
+ if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}");
+
return this.ReadBytes(len);
}
public byte[] ReadBytes(int length)
{
+ if (this.BytesRemaining < length) throw new InvalidDataException($"Read length is longer than message length: {length} of {this.BytesRemaining}");
+
byte[] output = new byte[length];
Array.Copy(this.Buffer, this.readHead, output, 0, output.Length);
this.Position += output.Length;
while (readMore)
{
+ if (this.BytesRemaining < 1) throw new InvalidDataException($"Read length is longer than message length.");
+
byte b = this.ReadByte();
if (b >= 0x80)
{